views:

94

answers:

2

I'm sorting a list of <li> elements. I adapted a function on a page that I can't link right now. You can see the live site here. Click on "Sort and Filter" and click any of the Sort by methods (English name, material, status...). All sorts are essentially the same, so CPU time is the same.

My jQuery function looks like this:

jQuery.fn.sort = function() {
    return this.pushStack([].sort.apply(this, arguments), []);
};
  function sortAscending1(a, b)  { return $(a).find(".english").text() > $(b).find(".english").text() ? 1 : -1; };  
  function sortDescending1(a, b)  { return $(a).find(".english").text() < $(b).find(".english").text() ? 1 : -1; };

And I'm calling it from the following jQuery line (s_alf_eng is a clicable div on the page).

$(document).ready(function() {
    $(".s_alf_eng").toggle(
        function() {
            $('.media-status-specie li').sort(sortAscending1).appendTo('.media-status-
        },
        function() {
            $('.media-status-specie li').sort(sortDescending1).appendTo('.media-status-specie');
        });

I can provide any extra clarification quickly. Thanks!

EDIT: The question is that executing this sort for large list takes several seconds. In my core2duo it might take up to 20 seconds! I changed the find(".english") to filter(".english") and the speed seems to be the same. Any idea on how to speed that up?

+2  A: 

Make a copy in memory of the list and sort it, then replace the existing list with the sorted one. That would be faster than manipulating live, visible, DOM elements.

Jeff Ober
You mean like having the list 4 or 5 times (one for each sort) and just switch the display of them? That could be an option, but I wonder if it would make the page much heavier.
Ferran Gil
I use this technique on the list of up to 500ish business listings at http://www.keytosavannah.com/listings/browse/category/things_to_do/. Javascript is minified but basically I push the data into an array on page load and print it to the results div as needed by the pagination. The downside to this method is that you have nothing to show the search engine spiders unless you make other arrangements.
Ty W
No, not create a copy for each sort. Create the copy on the fly, when the sort is required. Or, create on single copy in memory and use it whenever a sort is needed. That would cause complications, though, if the list can change dynamically.
Jeff Ober
I think I get the point. What I'm doing now is sorting the visible elements. Thus, sorting by a, then by b, then by a, then by b... requires always the same time. Creating a copy on the fly, and then (once sorted) hide the original and show the newly created one. Switching between sort a, sort b, sort a.. would be as fast as hiding a div containing all <li> elements. That's the point?
Ferran Gil
It would be faster, because there would not be the added overhead of the browser updating the visible page on each iteration of the sort. The price is the memory used to hold the copy.Note - you would delete the old list and insert the new one in its place, not just hide the old list (unless it is already in an order that is optimized for your sort algorithm).
Jeff Ober
yes, the original list is also the "sort a". The user might want to re-sort it as it was. We only have 5 different sorts, so I think the memory problem is small (and we are talking about memory on the users browsers, right?).
Ferran Gil
Yes, but if you have a large number of elements, the memory *might* be an issue. Another way to deal with it is to write the items out as a large JSON object, then dynamically introduce them to the page as needed. That way, the HTML is just a view of the data. A sort would work on the JSON object, delete the contents of the visible list, then write out the new list. A filter would do the same thing.
Jeff Ober
Very interesting. Currently, the filters just hide() the <li>'s that needs to be hidden. The other answer is fast enough for the size of our data, so for now I think I'm done with this. Thank you for all the comments.
Ferran Gil
A: 

I think if you add name as metadata to the node and eliminate looking through the DOM on every comparison, it'll be much faster. Remember, there's O(n2) of them after all.

$(function() { 
    $('.media-status-specie li')
        .each(function () {
            $(this).data('name', $(this).find(".english").text());
        })
});

.... 

function sortDescending1(a, b)  { 
    return $(a).data('name') < $(b).data('name') ? 1 : -1; 
}

Disclaimer: It's my opinion, I'm no good at guessing slow points as any other developer out there, use profiler to find real performance killer.

vava
This is much faster! I have 5 different sorts (10 functions).I think I can *parse* all the list and fill more variables like: .each(function () { $(this).data('english', $(this).find(".english").text()); $(this).data('scientific', $(this).find(".scientific").text()); $(this).data('total', $(this).find(".total-material").text()); })Is this going to increase the memory of the page? The largest page is below 500 <li> elements.
Ferran Gil
Sure it will increase the memory consumption. Not really sure if it would be a problem though and it will heavily depend on the browser. You'd have to run few experiments to find out.
vava
BTW you can assign value of any type, so data('sorting params', {english: 'name', scientific: 'bird'}) will work too.
vava
It's already on the live site! It's performing much better. It even works good on IE6 :) Many thanks!
Ferran Gil