WayneF
WayneF

Reputation: 255

Sorting a HTML table with Array#sort

I know this site is a great resource that offers methods of sorting a HTML table by coding the sort and element swap myself. I use that, and it works fine. My goal is to NOT add an included library, but was imagining possibly a better method might be as follows:

Where tr = document.getElementById("table1").rows,
an element is accessed as tr[index].cells[0].textContent

So this tr looks as if it is an array of rows.

However

tr = document.getElementById("table1").rows;  
tr.sort(function(a, b) {            
  return b.cells[0].textContent - a.cells[0].textContent;    //order high to low   
})

fails, because it says tr.sort is not a function. And table.rows does seem defined as an Object instead of an Array. Don't know how that differs, but rows looks and acts like an array.

Is there some workaround to allow this array method to work with Javascript sort?

Response to question in the comment:

Table is complicated because each row item is actually two adjacent rows. Plus a few TD are blank, causing numeric NaN errors unless handled. I only sort first row of 2, but move two rows. But I do have additional simple ordinary tables that could be sorted. 17 columns, one row looks like

<tr><td><a href="https://www.morningstar.com/funds/xnas/vfiax/performance">VFIAX</a>
><td>5.47<td>-36.97<td>26.62<td>15.05<td>2.08<td>15.96<td>32.33<td>13.64<td>1.36<td>11.93<td>21.79<td>-4.43<td>31.46<td>18.37
><td>13.34<td><b>S&P 500 Index</b><td>
<tr><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td><td>

and my current code is

function sortTable(tr, sort_col) {                              
  var len, i, x, y, a, b;   
  len = tr.length - 2;   
  i = 1;   
  while (i < len) {   
    for (i = 1; i < len; i += 2) {  //double rows for each item
      a = tr[i].cells[sort_col].textContent;   
      b = tr[i + 2].cells[sort_col].textContent;    //text for ticker in col 0   
      if (sort_col > 0) {   
        a = parseFloat(a); //parseFloat because it is reading string fields like 12.6%   
        b = parseFloat(b);   
        if (isNaN(a)) a = 0;    //early blank values cause NaN errors   
        if (isNaN(b)) b = 0;   
      }   
      if ( (sort_col == 0)? a > b : a < b) {   
        tr[i].parentNode.insertBefore(tr[i + 2], tr[i]);   
        tr[i+1].parentNode.insertBefore(tr[i + 3], tr[i + 1]);  //2nd of double rows   
        break;   
      }   
    } // i   
  }   
  Sorted = 1;   
}

Upvotes: 2

Views: 3967

Answers (2)

WayneF
WayneF

Reputation: 255

This is NOT another answer, but just lengthy comments on trincots wonderful solution. I had a 100 row table with sorting, but it had one option where each row element became two rows which had to sort together. So for the double rows, I created a BubbleSort routine. But it finally dawned on me how to use trincots solution (and I also kept the Bubblesort only for the two row case).

The timing varies a bit each time, but it's several times faster, 5 to 8x. I also had the header issue (both top and repeated bottom header), and a lower case alpha case, so had to modify it a bit. In case it may be of interest, this is what I figured out. It does work well. I'm no expert, so I'd appreciate hearing about any errors or efficiency improvements. Thank you trincot

//trincot's original, which is wonderful
let table = document.getElementById("table1");
let trs = table.rows;
Array.from(trs)
     .sort((a, b) => a.cells[0].textContent - b.cells[0].textContent)
     .forEach(tr => table.appendChild(tr));


//My HTML   (I already had the tbl and trs var)
//let t1 = performance.now();
        if (optn_row_spacing == 1) {    //if only single rows in table vs. two rows per element
            Array.from(trs)
              .sort((a, b) => JavaScriptSort(a, b))     //call comparison function
              .forEach(tr => tbl.appendChild(tr));
        }
        else BubbleSortTable(trs, ifBinary, );      //two row elements, which can be binary or alpha columns
//console.log("Sort " + optn_row_spacing + " took " +  (performance.now() - t1) + " milliseconds. Span Btn " + spn + ". Optn Btn " + btn);


//Javascript sort function for a, b comparison
function  JavaScriptSort(a, b) {    // if single rows, so elements are NOT double row groups
    if (a.rowIndex == 0 || b.rowIndex == bottom_header_row)     //leave both headers in place
        return 0;       //needed row number to recognize headers
    var a2 = a.cells[sort_btn_col].textContent,
        b2 = b.cells[sort_btn_col].textContent;
                                    //sort_btn_col is global access
    if (sort_btn_col == 0)          //col 0 & 12 are text
        return a2 > b2;             //0 IS ALL UPPER CASE TEXT
    if (sort_btn_col == 12)         //12 is Mixed Case which didn't sort right
        return a2.toLowerCase() > b2.toLowerCase();
    //else numeric.. The parseFloat because it's reading fields ending with "%"
    return parseFloat(b2) - parseFloat(a2);     //decreasing size
}

Upvotes: 0

trincot
trincot

Reputation: 350272

You can use Array.from to get the td elements in an array that can be sorted. Then add each of them again to the table in that order. This will be understood as a move in the DOM, so you'll end up with sorted table rows:

let table = document.getElementById("table1");
let trs = table.rows;

Array.from(trs)
     .sort((a, b) => a.cells[0].textContent - b.cells[0].textContent)
     .forEach(tr => table.appendChild(tr));
<table id="table1">
   <tr><td>2</td></tr>
   <tr><td>4</td></tr>
   <tr><td>3</td></tr>
   <tr><td>1</td></tr>
</table>

Upvotes: 5

Related Questions