Tuesday 7 April 2009

Javascript Sorting

OK this came up today in a skype chat about browser side sorting of array's returned by AJAX, prior to display on the page. particularly useful if you want the user to have the ability to sort the date in a variety of ways without going back to the server to get it done. The great thing is there is a built in JS method on the ARRAY object that allows you to sort the contents in all sorts of interesting ways.

It is relatively simple to do but there are a few GOTCHAs to remember the main one of these being that sort() sorts lexicographically! So....
myArray = ["Bob","Anna","Zeebedee","George"]
myArray.sort()
...will result in myArray contents = ["Anna","Bob","George","Zeebeedee"]

Thats all fine and dandy however what happens when you do this?
myArray = [12,400,3,25]
myArray.sort()
You will get myArray = [12,25,3,400]!
Why???WTF??? I hear you cry.. this happens because it a lexicographic sort and lexicographically 3 is bigger than 25 ...go figure! But all is not lost to get arround this you would pass a function to the sort() method.

When such a function is passed into array.sort(), the array elements are sorted based on the relationship between each pair of elements "a" and "b" and the function's return value. The three possible return numbers are: less than 0, 0, or greater than 0

  • Less than 0: Sort "a" to be a lower index than "b"
  • Zero: "a" and "b" should be considered equal, and no sorting performed.
  • Greater than 0: Sort "b" to be a lower index than "a".

So to sort an array numerically and ascending for example, the body of your function would look like this:

myArray = [12,400,3,25]
myArray.sort(sortMyNumbersAsc)

function sortMyNumbersAsc(a,b) { return (a-b) }

This works the way it does because whenever "a" is less than "b", a negative value is returned, which results in the smaller elements always appearing to the left of the larger ones, in other words, ascending.

Sort an array numerically but descending isn't much different, and just requires reversing the two operands "a" and "b" into this

myArray = [12,400,3,25]
myArray.sort(sortMyNumbersDsc)

function sortMyNumbersAsc(a,b) { return (b-a) }

You can also have fun shuffling the contents of your array by randomising the entries, i don't actually test "a" and "b" I just return a value of less than 0, 0, or greater than 0

var myarray=[1996, "Duck", 28.445, "Gertrude's Lemon Corporation"]
myarray.sort(scrambleMe)

function scrambleMe(a.b) { return (0.5 - Math.random()) }
So that is what you do when you have flat single dimension arrays, what happens when I have a multiple dimension array -- can I still sort? Yes you can and here is how... conside this array
var myArray = new Array();
myArray.push( ["one",1]);
myArray.push( ["three",3]);
myArray.push( ["four",4]);
myArray.push( ["two",2]);
myArray.sort(sortMe);
Now when you pass a and b to the function the entire dimension is passed so you can refer to the index value to do the sort, like this is we want to sort ascending on the second numeric dimension
function sortMe(a,b)
{
x = a[1];
y= b[1];
return (x-y)
}
This will return the array sorted like this

["one",1]
["two",2]
["three",3]
["four",4]

Simple ;-)

Enjoy!

Disqus for Domi-No-Yes-Maybe