Sorting rows by words and numbers in JavaScript explained

Hello, I'm a beginner and I need help sorting the table rows in the following table. I also want to attach an onclick listener to the header after it is displayed.

ID Name Inventory Volume
1 Rachel Data is not enough
2 Ross 100
3 Monica 1
4 Connor Data is not enough
5 Dustin -5

I would like to sort this table with the words at the end in descending order.

ID Name Inventory Volume
2 Ross 100
3 Monica 10
5 Dustin -5
1 Rachel Data is not enough
4 Connor Data is not enough

In addition, I'd like the other columns to be sorted as well and have the function to sort them too.

I tried a solution but it only works for one column. Here is the link to my code: https://jsfiddle.net/7wnke5q2/

function sortData(data, method) {
  let lessData = 'Data Not Enough'
  let lastItems = []
  let sortedList;
  if (method == 'descending') {
    sortedList = data.sort((a, b) => {
      return a - b
    })
  } else if (method == 'ascending') {
    sortedList = data.sort((a, b) => {
      return b - a
    })
  }
  for (let i = 0; i < sortedList.length; i++) {
    if (sortedList[i] == lessData) {
      let item = sortedList[i]
      sortedList.splice(i, 1)
      sortedList.push(item)
    }
  }
  sortedList = sortedList.concat(lastItems)
  return sortedList
}

I would really appreciate your help. Thank you!

Answer №1

If my understanding is correct, you are looking to have all instances of "Data Not Enough" at the end of the sorted list. Here is a function that can achieve this:

function sortData(data, method) {
    let lessData = 'Data Not Enough'
    let sortedList;  
    sortedList = data.sort((a, b) => {
        if(a=='Data Not Enough')
            return 1
        if(b=='Data Not Enough')
            return -1
      return method=='ascending'? a-b : b-a
      })
    return sortedList
}

Answer №2

Is that similar to what you were looking for?
Simply click on the header columns to organize the table

const 
  myTableHead     = document.querySelector('#my-table thead')
, myTableHead_TH  = document.querySelectorAll('#my-table thead th')
, myTableBody     = document.querySelector('#my-table tbody')
, myTableBody_TR  = [...document.querySelectorAll('#my-table tbody tr')]
, sortOrder       = ['','asc','desc']
, isDNE = str => str.trim() === 'Data is not enough'
  ;
myTableHead.onclick = ({target}) =>
  {
  if (!target.matches('th')) return
  let idx = (sortOrder.findIndex(x=>x===target.className) +1) %3
  myTableHead_TH.forEach(th=>th.className='')
  target.className = sortOrder[idx]

  if ( sortOrder[idx] )
    {
    myTableBody_TR
      .sort(dynaSort(target.cellIndex, target.dataset.type, sortOrder[idx] ))
      .forEach(tr=>myTableBody.appendChild(tr) )
    }
  }

function dynaSort( colIndex, colType, order='asc' )
  {
  let sortOrder = (order === 'desc') ? -1 : 1
 
  return function(row_a,row_b)
    {
    let a = row_a.cells[colIndex].textContent
      , b = row_b.cells[colIndex].textContent
      ;
    if (isDNE(a) && isDNE(b)) return 0
    if (isDNE(a))  return +1
    if (isDNE(b)) return -1

    if (colType==='str')  return (a.trim().localeCompare(b.trim())) *sortOrder 
    return (Number(a) - Number(b)) *sortOrder 
    }
  }
table  {
  border-collapse : collapse;
  margin          : 2em 1em;
  font-family: Arial, Helvetica, sans-serif;
  }
td,th  {
  padding    : .2em .8em;
  border     : 1px solid darkblue;
  }
th::after {
  display    :  block;
  float      : inline-end;
  content    : '\25B7';
  margin     : 0 0 0 1em;
  transition : 180ms;
  color      : transparent;
}
th.asc::after {
  transform  : rotate(-90deg);
  color      : whitesmoke;
}
th.desc::after {
  transform  : rotate(+90deg);
  color      : whitesmoke;
}
thead {
  background : #437c97;
  color      : whitesmoke;
  cursor     : pointer;
  }
<table id="my-table" >
  <thead>
    <tr>
      <th data-type="num">ID</th> 
      <th data-type="str">Name</th>  
      <th data-type="num">Inventory Volume</th>  
    </tr>
  </thead>
  <tbody>
    <tr> <td>1</td> <td>Rachel</td> <td>Data is not enough</td> </tr>
    <tr> <td>2</td> <td>Ross</td>   <td>  100 </td>             </tr>
    <tr> <td>3</td> <td>Monica</td> <td>    1 </td>             </tr>
    <tr> <td>4</td> <td>Connor</td> <td>Data is not enough</td> </tr>
    <tr> <td>5</td> <td>Dustin</td> <td>   -5 </td>             </tr>
  </tbody>
</table>

Answer №3

  • This should work perfectly! Utilizing the @YamirL sorting algorithm.
function sort(e, method) {
  //Get table
  while ((e = e.parentElement) && !e.classList.contains("table"));
  //Get rows
  let rows = Array.from(e.getElementsByTagName("tr"));
  //Get each value for each row
  let values = rows.map(row => {
    let tds = Array.from(row.getElementsByTagName("td"));
    return tds.map(td => td.innerHTML);
  });
  
  // Remove headers from values array
  values.shift();

  // Sort the array by volume
  values.sort((a, b) => {
    var exception = "Data Not Enough";
    if(a[1] == exception)
      return 1;
    if(b[1] == exception)
      return -1;
    return method == 'ascending' ? a[1] - b[1] : b[1] - a[1];
  });

  // Put sorted values back on the table

  // Get body
  let body = e.getElementsByTagName("tbody")[0];
  // Erase Body
  body.innerHTML = "";
  // Iterate each row
  values.forEach(row => {
    // Create new row element
    let tr = document.createElement("tr");
    // Iterate each column
    row.forEach(val => {
      // Create new value
      let td = document.createElement("td");
      // Append values
      td.append(val);
      tr.append(td);
    });
    // Append row to body
    body.append(tr);
  });
}

Here is a snippet to test the complete code, I included a <tbody> in the table.

function sort(e, method) {
  while ((e = e.parentElement) && !e.classList.contains("table"));
  let rows = Array.from(e.getElementsByTagName("tr"));
  let values = rows.map(row => {
    let tds = Array.from(row.getElementsByTagName("td"));
    return tds.map(td => td.innerHTML);
  });

  values.shift(); 

  values.sort((a, b) => {
    var exception = "Data Not Enough";
    if(a[1] == exception)
      return 1;
    if(b[1] == exception)
      return -1;
    return method == 'ascending' ? a[1] - b[1] : b[1] - a[1];
  });

  
  let body = e.getElementsByTagName("tbody")[0];
  body.innerHTML = "";
  values.forEach(row => {
    let tr = document.createElement("tr");
    row.forEach(val => {
      let td = document.createElement("td");
      td.append(val);
      tr.append(td);
    });
    body.append(tr);
  });
}
.clickable {
  cursor: pointer;
}

.clickable:hover {
  opacity: 0.7;
}
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>


<table class="table">
  <thead>
    <tr>
      <th scope="col">Name</th>
      <th scope="col" class="clickable" onclick='sort(this, "descending")'>Volume</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Joey</td>
      <td>Data Not Enough</td>
    </tr>
    <tr>
      <td>Ross</td>
      <td>-5</td>
    </tr>
    <tr>
      <td>Monica</td>
      <td>1</td>
    </tr>
    <tr>
      <td>Ben</td>
      <td>100</td>
    </tr>
    <tr>
      <td>Chandler</td>
      <td>Data Not Enough</td>
    </tr>
  </tbody>
</table>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

CSS does not have the capability to style child elements

Having trouble changing the text for child elements when applying CSS classes to parent elements. Is there a specific reason why this is happening? Take a look at my current code: <Box //not affecting all child elements ...

Interact with multidimensional arrays using Vue JS

Is there a way to access properties within objects nested inside multidimensional arrays when using v-for with VueJS? var arr =[{"fruit": {"fruitName": "apple"}, "vegetable":[{"vegetableName": "carrot" }]}]; I am attempting to display it in the following ...

What is the best way to activate a component within Angular 2 that triggers the display of another component through method invocation?

I have created a popup component that can be shown and hidden by calling specific methods that manipulate the back and front variables associated with the class. hide() { this.back = this.back.replace(/[ ]?shown/, ""); this.front = this.front.replace( ...

The copyright (©) symbol is unresponsive to rotation

Why can't I rotate the © character? Am I missing something? .copy { font-size: 12px; font-family: Arial; writing-mode: vertical-rl; text-orientation: mixed; transform: rotate(180deg); } <span class="copy">&copy; This is not ...

Tips for integrating external JavaScript libraries and stylesheets into a widget

I am currently working on developing a custom Javascript widget that requires users to insert specific lines of code into their web pages. This code will then dynamically add an externally hosted javascript file, allowing me to inject HTML content onto the ...

Use PHP to create a new JSON file on the server by submitting a form, then utilize a second form to update

My current goal is to create a json file using a form and then update that file with another form. Both of these processes are handled in the process.php file. I have managed to successfully update the json file if it is named as data.json initially in pro ...

Python allows for the sending of HTML content in the body of an email

Is there a way to show the content of an HTML file in an email body using Python, without having to manually copy and paste the HTML code into the script? ...

Troubleshooting node modules for browser compatibility

Looking for assistance with running a specific node module in a browser. The module in question is called fury.js. I attempted to use browserify, however, encountered an error stating "ReferenceError: fury is not defined" when trying to utilize it. In th ...

The issue with height percentages being ineffective in CSS

My goal is to utilize the height property with percentages, however it seems to be ineffective. I desire to use percentages so that the layout appears well in various resolutions. <div id="bloque_1" style="height: 80%;background: red"> </div> ...

Ensure that the Popover vanishes upon scrolling the page - Material UI (MUI) v5 compatibility with React

When implementing the MUI v5 standard pattern for displaying a popover upon hovering over another element, everything works smoothly except for one scenario: If you hover over the element And without moving the mouse, use the scroll wheel to scroll throug ...

Execute a function before the page reloads in ASP.NET with the help of JQuery

Is there a way to call a function before postback in Asp.Net using JQuery? ...

What is the best way to delete an added element once a DIV has been toggled with JQuery?

I'm facing an issue where I need to add an element to a DIV that has a toggle function. However, every time I click the toggle again after adding the element, the same element is appended once more, resulting in duplicate elements. Upon observation, ...

Struggles encountered when choosing the initial visible item

I have a set of 3 tabs, each with its own heading and content. However, I only want to display the tabs that the user selects by checking the corresponding checkboxes. There are 3 checkboxes, one for each tab. Below is the code snippet: //Function to ...

"Resetting count feature in AngularJS: A step-by-step guide

I have a list consisting of four items, each with its own counter. Whenever we click on an item, the count increases. I am looking to reset the counter value back to zero for all items except the one that was clicked. You can view the demonstration here. ...

Tips for creating a mandatory textarea input field

It's pretty straightforward - I have a textarea that is set to required, but it only alerts the user if you actually click inside the text area. If you try to submit without clicking inside, it won't prompt the alert. Take a look at this Fiddle ...

What is the best way to showcase every user on my webpage using HTML?

After running this code snippet in my python terminal, I was able to see the desired output: views.py from django.contrib.auth.models import User userList = User.objects.all() print(userList) The above code produces the following result in the terminal: ...

Initiate an animation in Wordpress once the entire page has finished loading

Recently, I incorporated some "raw html" elements with animations into my WordPress site. However, the issue I'm facing is that these animations kick off as soon as the page loads, without waiting for the preloader to complete and display the actual c ...

Adding HTML components to an image with adjustable dimensions

A graphic designer has provided us with an image featuring three selection boxes. I implemented the necessary HTML elements and CSS to display three overlapped selection boxes on top of the image, using pixel values for positioning. However, the original ...

How to modify values in a JSON array using JavaScript

Currently, I am facing an issue with displaying dates properly on the x-axis of a graph created using Highcharts. To solve this problem, I need to parse the dates from the JSON response. Despite my attempts to manipulate the JSON date, I have not been able ...

PHP code in Wordpress incorporating an Ajax request

Does anyone know how to fetch user data as a string using ajax in WordPress? I think I understand the basic concept PHP This code goes in my functions.php file add_action('template_redirect', 'edit_user_concept'); function edit ...