Reputation: 16924
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?
Upvotes: 9
Views: 15571
Reputation: 54628
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;
}
});
Sure your loops are the same, but much less processing per loop.
Upvotes: 3
Reputation: 3939
If your comparison function is expensive, the standard method is the schwartzian transform (seen in some comments, but without an actual example):
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.
Upvotes: 1
Reputation: 16924
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
Upvotes: 4
Reputation: 42496
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.
Upvotes: 2
Reputation: 630429
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.
Upvotes: 6