Home > front end >  Sort table rows by TWO variables
Sort table rows by TWO variables

Time:01-27

I am trying to dynamically sort a table with javascript/jQuery. The table rows have 2 attributes:

  1. deadline (unix timestamp)
  2. status (1 or 2)

The table needs to be sorted by status (status 1 on top, status 2 on the bottom), and then by deadline (ascending).

For example, the correct order of an example of rows would be:

<table id="todo_table">
  <tbody>
    <tr deadline="1633333333" status="1"></tr> 
    <tr deadline="1644444444" status="1"></tr>
    <tr deadline="1611111111" status="2"></tr>
    <tr deadline="1622222222" status="2"></tr>
  </tbody>
</table>

However, due to rows being added and removed dynamically by javascript (or by deadlines or statusses being changed), I want to have a function that re-sorts the table after all these actions.

I was able to write (rather: find on the internet) a piece of code that sorts the table based on 1 attribute:

var $table= $('#todo_table');
var rows = $table.find('tr').get();
rows.sort(function(a, b) {
  var keyA = $(a).attr('deadline');
  var keyB = $(b).attr('deadline');
  if (keyA < keyB) return -1;
  if (keyA > keyB) return 1;
  return 0;
});
$.each(rows, function(index, row) {
  console.log(row);
  $table.children('tbody').append(row);
});

However, I cannot figure out how to sort by a second attribute at the same time (while preserving the sort on the other attribute).

Here's my snippet:

function sort_table(){

  var $table= $('#todo_table');
  var rows = $table.find('tr').get();
  rows.sort(function(a, b) {
    var keyA = $(a).attr('deadline');
    var keyB = $(b).attr('deadline');
    if (keyA < keyB) return -1;
    if (keyA > keyB) return 1;
    return 0;
  });
  $.each(rows, function(index, row) {
    $table.children('tbody').append(row);
  });
    
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<table id="todo_table">
  <tbody>
    <tr deadline="1622222222" status="2"><td>Todo 4</td></tr>
    <tr deadline="1611111111" status="2"><td>Todo 3</td></tr>
    <tr deadline="1644444444" status="1"><td>Todo 2</td></tr>
    <tr deadline="1633333333" status="1"><td>Todo 1</td></tr>   
  </tbody>
</table>

<button onclick="sort_table()">SORT</button>

The end result should be that the rows in this snippet are sorted Todo 1 - Todo 2 - Todo 3 - Todo 4, but right now, because it only sorts on "deadline", the order is 3 - 4 - 1 - 2.

Any help would be greatly appreciated :-)

CodePudding user response:

You can sort by multiple attributes by chaining the sorting conditions. In this case, you want to sort by status first, and then by deadline. I'm not sure if this is the best way.

function sort_table(){
  var $table= $('#todo_table');
  var rows = $table.find('tr').get();
  rows.sort(function(a, b) {
var keyA = $(a).attr('status');
var keyB = $(b).attr('status');
if (keyA < keyB) return -1;
if (keyA > keyB) return 1;
var keyA = $(a).attr('deadline');
var keyB = $(b).attr('deadline');
if (keyA < keyB) return -1;
if (keyA > keyB) return 1;
return 0;
  });
  $.each(rows, function(index, row) {
$table.children('tbody').append(row);
  });
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<table id="todo_table">
  <tbody>
    <tr deadline="1622222222" status="2"><td>Todo 4</td></tr>
    <tr deadline="1611111111" status="2"><td>Todo 3</td></tr>
    <tr deadline="1644444444" status="1"><td>Todo 2</td></tr>
    <tr deadline="1633333333" status="1"><td>Todo 1</td></tr>   
  </tbody>
</table>

<button onclick="sort_table()">SORT</button>

CodePudding user response:

This solution produces the same result as Gihan's answer, only using some builtin functions, like JQuery's sort and empty, along with modern browsers' localeCompare to sort the strings, since it returns a value as expected by sort anyways:

referenceStr.localeCompare(compareString) returns a negative number if referenceStr occurs before compareString; positive if the referenceStr occurs after compareString; 0 if they are equivalent.

While it works, I'd recommend using data-* attributes (e.g. data-deadline instead deadline) to make sure you're not clashing with any standard HTML attribute.

function sort_table() {
  const tableBody = $("#todo_table > tbody");
  
  // Find & sort table rows
  const rows = tableBody
    .children()
    .sort((rowA, rowB) => {
      const a = toSortAttributes(rowA);
      const b = toSortAttributes(rowB);

      const deadlineOrder = a.deadline.localeCompare(b.deadline);
      const statusOrder = a.status.localeCompare(b.status);     

      if (statusOrder === 0) {
        return deadlineOrder;
      } else {
        return statusOrder
      }
    });

  // Remove old rows, replace with sorted ones
  tableBody
    .empty()
    .append(rows);
}

function toSortAttributes(row) {
  const deadline = $(row).attr("deadline");
  const status = $(row).attr("status");
  
  return { deadline, status };
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<table id="todo_table">
  <tbody>
    <tr deadline="1622222222" status="2"><td>Todo 4</td></tr>
    <tr deadline="1611111111" status="2"><td>Todo 3</td></tr>
    <tr deadline="1644444444" status="1"><td>Todo 2</td></tr>
    <tr deadline="1633333333" status="1"><td>Todo 1</td></tr>   
  </tbody>
</table>

<button onclick="sort_table()">SORT</button>

  • Related