Sort of Confusing

Some of the worst JavaScript code I have seen has to do with sorting data.

JavaScript has a flexible array sort method, but it takes a while to get the hang of using it. Programmers seem to do fine sorting a simple array, but if the data format is something just a bit less trivial, many JavaScript programmers throw their hands up in the air. You can almost see the frustration in the code (sometimes in the comments).

One common situation that many people seem stumped by is sorting an array of objects based on a given key.

Suppose we have this array of objects:

    arr[0]={r:100,g:100,b:250};
    arr[1]={r:50,g:127,b:255};
    arr[2]={r:255,g:255,b:100};

Now suppose you want to sort the array by any of r, g, or b, and that you want to support both ascending and descending sorts. How would you approach the problem?

Back To Basics

Remember that to sort an array, you just invoke the “sort” method that belongs to JavaScript’s array type.

    arr.sort();

That works for sorting an array of strings, but it doesn’t work for sorting an array of numbers. That’s why even the novice JavaScript programmer recognizes the code for sorting numeric values.

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

    arr.sort(sortNumber);

JavaScript lets us pass in a function that is used to judge whether a value is less than, equal, or greater than another value.

Having that hook into the guts of the sort method lets us solve just about any sorting problem. But how?

Here’s a solution for the {r,g,b} problem above, where I want to be able to sort on r, g, or b in either ascending or descending mode.

    function sortByKey(arr,key,direction) {
        function sortNumeric(a,b) {
            if (direction=="ASC") {
                return a[key]-b[key];
            } else {
                return b[key]-a[key];   
            }
        }       
        return arr.sort(sortNumeric);
    }

The outer function is a handy way to keep the key (what we’re sorting on) and direction (which way we’re sorting) around, since there is no simple way to pass that information into the sort method. Remember that the inner function has access to the outer function’s variables, and that includes the parameters that come in.

Oh, by the way, the code is easier to understand if you recall that obj.r is the same as obj[“r”]. We can pass in a string to a function and use that string as a key to get a value. That way we get around the need to write a separate evaluation function for each key.

Call the function like this:

    sortByKey(arr,"r","DESC");

to sort the objects in descending r order, or

    sortByKey(arr,"g","ASC");

to sort the objects in ascending g order.

Real Life Intrudes

Sometimes the data is not nice. You may have blank (empty or missing) spots in your data. In Excel, whether you sort ascending or descending, cells that are empty always show up at the end of the list. I recently needed to match that functionality in an Adobe AIR utility program I was writing. Here’s the code (blank cells from Excel ended up as empty strings in my data).

    function sortByKey(arr,key,direction) {
        function sortNumeric(a,b) {
            var c=a[key],d=b[key];
            if (direction=="ASC") {
                return (c==="")-(d==="") || c-d;
            } else {
                return (c==="")-(d==="") || d-c;   
            }
        }       
        return arr.sort(sortNumeric);
     }

A bit longer, codewise. But exactly what I needed for my data.

Once you get the hang of using inner functions, JavaScript’s sort method makes much more sense. It’s easy to extend this idea to accomodate other kinds of data (like strings), or to support secondary sorts, where a second key can be used to break ties.