9

I have some simple jQuery written to sort some elements based on a numerical attribute as illustrated at http://jsfiddle.net/MikeGrace/Vgavb/

// get array of elements
var myArray = $("#original div");

// sort based on timestamp attribute
myArray.sort(function (a, b) {

    // convert to integers from strings
    a = parseInt($(a).attr("timestamp"), 10);
    b = parseInt($(b).attr("timestamp"), 10);

    // compare
    if(a > b) {
        return 1;
    } else if(a < b) {
        return -1;
    } else {
        return 0;
    }
});

// put sorted results back on page
$("#results").append(myArray);

It works fine but I don't think it will scale because a total of 185 jQuery calls are made, 184 of them which are getting the attribute of an element to do the comparison.

What is a more efficient way to do this sorting with jQuery?

1

5 Answers 5

6

If you're worried about performance, and know for sure where the attribute comes from, you can use the DOM methods directly, .getAttribute() in this case:

a = parseInt(a.getAttribute("timestamp"), 10);
b = parseInt(b.getAttribute("timestamp"), 10);

You can test the difference here, as you see it's more than a little speed boost, .attr() does a lot of extra work internally, ultimately calling .getAttribute() itself in this case anyway.

Sign up to request clarification or add additional context in comments.

3 Comments

This is still retrieving the attribute for each element. Is there a way so I don't have to get the attribute for each comparison?
@Mike - you could loop through it and store each in an object map, yes...but is that more efficient? you'd have to test, that iteration over the elements wouldn't be free, you'd have to compare the cost...always test!
@Mike Grace you just answered your own question. Get the attributes once, store them as part of an array of {timestamp, jquery}, sort on the timestamp.
4

If you need to do lots of sorting repeatedly, it would be good to work towards a more native approach that doesn't involve jQuery. Otherwise, there really isn't much that is going to make a noticeable difference.

Here are some tests trying a few different approaches -> http://jsperf.com/jquery-sort-by-numerical-property/2

1 Comment

Do you mean a native approach like the solution Nick Craver posted?
3

Using jQuery 1.6 Data() will be much faster.

First:

The data- attributes are pulled in the first time the data property is accessed and then are no longer accessed or mutated (all data values are then stored internally in jQuery).

This means accessing the specific data value after the first is internal so no DOM lookup nor parsing of the value needs to be done.

Second:

Every attempt is made to convert the string to a JavaScript value (this includes booleans, numbers, objects, arrays, and null). The string value "100" is converted to the number 100.

Now that the value is stored internally as a number, it does not need to be parsed every time either.

Html

<div id="original">
  <div data-timestamp="99">99</div>
  <div data-timestamp="999">999</div>
  <div data-timestamp="12">12</div>
  <div data-timestamp="11">11</div>
  <div data-timestamp="10">10</div>
  <div data-timestamp="9">9</div>
  <div data-timestamp="8">8</div>
  <div data-timestamp="7">7</div>
  <div data-timestamp="6">6</div>
  <div data-timestamp="5">5</div>
  <div data-timestamp="4">4</div>
  <div data-timestamp="3">3</div>
  <div data-timestamp="2">2</div>
  <div data-timestamp="1">1</div>
  <div data-timestamp="9999">9999</div>
</div>

Javascript

// sort based on timestamp attribute
myArray.sort(function (a, b) {

    // convert to integers from strings
    a = $(a).data("timestamp");
    b = $(b).data("timestamp");
    count += 2;
    // compare
    if(a > b) {
        return 1;
    } else if(a < b) {
        return -1;
    } else {
        return 0;
    }
});

Update jsFiddle Example

Sure your loops are the same, but much less processing per loop.

Comments

2

For what it's worth, I don't think you need to call the slow parseInt:

a = parseInt($(a).attr("timestamp"), 10);
a = +$(a).attr("timestamp");

If your timestamp attribute really is just a number, I think these will yield the same value.

1 Comment

True. jsperf.com/jquery-sort-by-numerical-property/2 Looks like removing the paresInt() call didn't change it much.
1

If your comparison function is expensive, the standard method is the schwartzian transform (seen in some comments, but without an actual example):

  1. extract the sort keys into [key, object] pairs
  2. sort by using the extracted keys
  3. extract the original objects

Example:

Array.prototype.schwartzian_sort = function (key_of) {
   var i;

   for (i = 0; i < this.length; ++i)
      this[i] = [key_of (this[i]), this[i]];

   this.sort (function (a, b) {
      a = a[0];
      b = b[0];

      return a < b ? -1
           : a > b ?  1
           :          0;
   });

   for (i = 0; i < this.length; ++i)
      this[i] = this[i][1];

   return this;
}

Instead of a comparison function, you pass a function that extracts srings or numbers:

myArray.sort(function (e) {
   return parseInt ($(a).attr ("timestamp"), 10);
});

This calls the parseInt and attr only once per element, which is always less than if called in a comparison function.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.