How to fix your number-sorting problems in Javascript

Did you ever wanted to sort [1, 10, null, 20] in Javascript and were the results unpredictable? It’s not a bug, but more something that’s not implemented. I came across this problem when testing some table sorting plugins for Dojo and jQuery. First I thought it were bugs, but the real problem lies in Javascript and the function you pass to the sort() method.

....

I’m using the Dojo filteringTable widget when I want to sort tables. It works fine but I had a problem with sorting a collection of numbers which contain null/empy values. The cells with null values appeared in the most strange places after sorting a column. First I thought it was a bug in Dojo (I’m sorry Dojo guys, but I had to blame someone 😉 ) and I did some investigation and got a bit stuck. A few months later I found out what my problem was (note that I did some other things in this period other than solving this problem)

To sort arrays in Javascript you use the sort method on an array.

var a = [0,null,10,11,null,20,100,null,-10,-1,111,2, null, 5];
a.sort();
console.log(a);

The result:

[-1, -10, 0, 10, 100, 11, 111, 2, 20, 5, null, null, null, null]


Well that looks fine. I prefer to have null values just before the 0, but that’s just because of the data I’m currently working with.
To do more advanced sorting you can add a sort function as a parameter

function numberAsc(a, b) {
     return a - b;
}

var a = [0,null,10,11,null,20,100,null,-10,-1,111,2, null, 5];

a.sort(numberAsc);
console.log("asc: ", a);

The result could be

asc: [-10, -1, null, null, null, 0, null, 2, 5, 10, 11, 20, 100, 111]

I said could because it’s quite fuzzy what happens. Let’s add a descending number function to illustrate this:

function numberAsc(a, b) {
    return a - b;
}

function numberDesc(a, b) {
    return b - a;
}

var a = [0,null,10,11,null,20,100,null,-10,-1,111,2, null, 5];

a.sort(numberAsc);
console.log("asc: ", a);
a.sort(numberDesc)
console.log("desc: ", a);
a.sort(numberAsc);
console.log("asc: ", a);
a.sort(numberDesc)
console.log("desc: ", a);

The result:

asc: [-10, -1, null, null, null, 0, null, 2, 5, 10, 11, 20, 100, 111]
desc: [111, 100, 20, 11, 10, 5, 2, 0, null, null, null, null, -1, -10]
asc: [-10, -1, null, null, null, null, 0, 2, 5, 10, 11, 20, 100, 111]
desc: [111, 100, 20, 11, 10, 5, 2, null, null, null, 0, null, -1, -10]

Wow! The second and third result look fine, but the first and last don’t. And because everything should look the same this is bad!

What you have to do to solve this problem is assigning a number to the null value in the sort function. I want null to appear just before 0, so I pick -0.00000001 (assuming there are no numbers with this many digits in the fraction part of a number)

The new function:

var ALMOST_ZERO = -0.00000001;

function numberAsc(a, b) {
    var left = a != null ? a : ALMOST_ZERO;
    var right = b != null ? b : ALMOST_ZERO;

    return left - right;
}

The terniary operator might be a bit confusing at first, but saves a lot of space. The lines with left en right assign -0.00000001 to the left or the right when any of these values is null.

When we run the script again the results look good:

asc: [-10, -1, null, null, null, null, 0, 2, 5, 10, 11, 20, 100, 111]
desc: [111, 100, 20, 11, 10, 5, 2, 0, null, null, null, null, -1, -10]
asc: [-10, -1, null, null, null, null, 0, 2, 5, 10, 11, 20, 100, 111]
desc: [111, 100, 20, 11, 10, 5, 2, 0, null, null, null, null, -1, -10]

When you want null to appear on front you need a very low value (ie. -999999999) and when null should appear on the end you take a very large number (ie.  999999999)

I’m quite happy I finally solved this problem. I started working on this issue again because I tried the new jQuery tablesorter plugin. In this article you can read more about it and how to apply the solution of this article to a beautiful table.