24

I have a table which is dynamically populated from FullCalendar. The problem is that FullCalendar does not care about its original order.

The table looks like this:

<table id="caltbl">
   <thead>
       <tr> <th> </th>   <th> Date </th>   <th> hours </th>  ... </tr>
   </thead>
   <tbody>
       <tr> <td class="sortnr">1</td>   <td></td> ... </tr>
       <tr> <td class="sortnr">3</td>   <td></td> ... </tr>
       <tr> <td class="sortnr">2</td>   <td></td> ... </tr>
       <tr> <td class="sortnr">4</td>   <td></td> ... </tr>
   </tbody>
</table>

The first of each row contains the number on which the table should be sorted.

I had this code to sort it:

    var rows = $('#caltbl > tbody').children('tr').detach();

    for (var counter = 1; counter<=rows.length; counter++) {
        $(rows).each(function(index) {
            if ($(this).find(".sortnr").text()==counter){
               $('#caltbl > tbody:last').append($(this));
            }
        });
    }

This works fine in Firefox but causes me a major headache in Internet Explorer because there are more than 500 items and it hangs. I could add a setTimeout but that would not fix the real problem. The sorting is slow. What is a faster way to sort this?

Instead of having to start from the <table> html, as I said it gets populated dynamically so I have an Array which contains the html. 1 item per <tr> (unsorted)

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Stefanvds
  • 5,868
  • 5
  • 48
  • 72
  • in IE 7 (and lower, I guess), any operations that involves adding elements to the DOM is very very slow... this would be the problem. How to solve it ... I am not quite sure. One solution is to do it server-side... I realized that sluggishness in jQuery autocomplete, and there are questions about it like : http://stackoverflow.com/questions/5073612/jquery-ui-autocomplete-combobox-very-slow-with-large-select-lists – tsimbalar Sep 26 '11 at 16:32
  • adding things to the DOM is not the problem. it works superbly fast, and it is higher than IE7. the problem really is the sorting. – Stefanvds Sep 26 '11 at 16:39
  • Googling "javascript sort" gives juuuust a few results. – Pete Wilson Sep 26 '11 at 16:46

5 Answers5

52

Fiddle: http://jsfiddle.net/qNwDe/

I've written an efficient, cross-browser method to sort the rows in your table. Multiple JQuery selectors in double loops are causing serious performance issues (as you've noticed), hence I've get rid of JQuery.

An additional advantage of my function is that it doesn't mind missing index numbers. I'm currently referring to the first cell of each row, rather than getting the element by class name. If you want to refer by classname, I will alter my function:

function sortTable(){
    var tbl = document.getElementById("caltbl").tBodies[0];
    var store = [];
    for(var i=0, len=tbl.rows.length; i<len; i++){
        var row = tbl.rows[i];
        var sortnr = parseFloat(row.cells[0].textContent || row.cells[0].innerText);
        if(!isNaN(sortnr)) store.push([sortnr, row]);
    }
    store.sort(function(x,y){
        return x[0] - y[0];
    });
    for(var i=0, len=store.length; i<len; i++){
        tbl.appendChild(store[i][1]);
    }
    store = null;
}

Call sortTable() whenever you want to sort the table.

Rob W
  • 341,306
  • 83
  • 791
  • 678
  • I guess this option is faster than mblase75's because it doesnt use the jQuery `find()` – Stefanvds Sep 26 '11 at 17:17
  • 2
    @Stefanvds mblase75's algoritm is highly inefficient, because it uses the `.find` function **inside** the `sort` function. Also, he's using `$("#calctbl > tbody")` **inside** the `for` loop, which adds up to several seconds at a very large table. As I stated in my answer, JQuery is a horrible solution for this case. – Rob W Sep 26 '11 at 17:21
  • 1
    I've put this into practice. It is mighty fast. Sorts 600 rows in a few seconds and IE does not hang. Brilliant! I knew jQuery is not made for this. That's why I've asked stackoverflow :) Thanks man! – Stefanvds Sep 26 '11 at 17:34
  • 5
    Just as a minor note, a table can have multiple `` elements. – Pointy Apr 01 '13 at 13:34
  • @Rob W Thanks for the function you provided above, I've been using it for a few weeks and its worked great. I've run into the issue where I am now trying to sort a table using your function but then also sorting any matching values found in the first column based on the values in another column. Basically using a "sub-sort for the matches found in the initial sort. Any input you could provide on altering your function to do that? Thanks – Twhyler Mar 30 '14 at 21:39
  • @Twhyler Store the extra column as an additional entry in the array, and adjust the sort function. See https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/sort – Rob W Mar 30 '14 at 21:48
  • Thanks for the help and quick response, I'll check out the link and see what I can figure out. – Twhyler Mar 30 '14 at 21:50
  • This somewhat answers the question, and somewhat doesn't, as it will only sort by numeric values, not by *whatever* is in the first column. If you know that, though, it's a great solution if it fits your problem! – TheThirdMan Dec 09 '17 at 12:35
7

Try an approach like this: http://jsfiddle.net/qh6JE/

var rows = $('#caltbl > tbody').children('tr').get(); // creates a JS array of DOM elements
rows.sort(function(a, b) {  // use a custom sort function
    var anum = parseInt($(a).find(".sortnr").text(), 10);
    var bnum = parseInt($(b).find(".sortnr").text(), 10);
    return anum-bnum;
});
for (var i = 0; i < rows.length; i++) {  // .append() will move them for you
    $('#caltbl > tbody').append(rows[i]);
}
Blazemonger
  • 90,923
  • 26
  • 142
  • 180
5

I think there are way too many loops in your case. With 500 items, you would loop 500*500 = 250000 times . Not so many browsers would know how to do that.

I suggest using the native array.sort() method of javascript to do the sorting based on a custom "comparison function".

Here is how it could be done (and most probably it can be optimized) : http://jsfiddle.net/tsimbalar/Dw6QE/.

The idea is to sort a list of rows comparing the sortNumber value ...

tsimbalar
  • 5,790
  • 6
  • 37
  • 61
1

We can use jquery insead of javascript for the same thing answered by Rob W. It will not affect any performance issue like Multiple JQuery selectors in double loops.

var $tbody = $('table tbody');
            $tbody.find('tr').sort(function (a, b) {
                var tda = $(a).find('td:eq(' + ColumnIndex + ')').text(); // Use your wished column index
                var tdb = $(b).find('td:eq(' + ColumnIndex + ')').text(); // Use your wished column index
                // if a < b return 1
                return tda > tdb ? 1
                       // else if a > b return -1
                       : tda < tdb ? -1
                       // else they are equal - return 0    
                       : 0;
            }).appendTo($tbody);

Use < instead of >for descending.

FIDDLE

SharmaPattar
  • 2,472
  • 3
  • 21
  • 23
  • Highly Efficient. It also arrange/reorder based on text/word of the first column – Abdullah Mamun-Ur- Rashid Sep 06 '15 at 13:37
  • This snippet sorts using String values (e.g. 1, 5, 44 will be sorted to 1, 44, 5). You can include `if(!isNaN(tda) && !isNaN(tdb)) return parseInt(tda) - parseInt(tdb);` before your return so that your code can also handle integer sorting. [Fiddle](http://jsfiddle.net/cc9xs69e/1/) – Himanshu Tyagi May 30 '16 at 19:48
0

Check out this http://square.github.com/crossfilter/ the team at Square has used a clever bitmap index technique to allow filtering 5.3MB data in <30ms ... I am not sure if this helps, but it is a very interesting technique

Deano
  • 1,136
  • 10
  • 19
  • Their `crossfilter.heap.sort()` function does not work, no idea why my comment was deleted. Try it yourself with an array filled with `10_000` random numbers from 0 to 10 before you delete my comment, or I'll just post it again. It's much faster than native sort, but you will see the beginning of the array gets sorted while the end gets ... less sorted. Unacceptable. – GirkovArpa Jul 28 '22 at 09:26