- Community Pick
- Experts Exchange Approved
Basic Sorting of Arrays
First, let's look at the basics. It's all about the sort() method of the Array object. The following example defines an array of person names, sorts them using the default handling, and displays the result:
As shown in that example, the sort is a "lexical" sort -- it sorts alphabetically in ascending (A-Z) order. That's great for sorting names, but not so wonderful when sorting numbers or other data types. For instance:
About the only thing that's good about that result is that it makes it easy for your JavaScript professor to give you a failing grade! It's even worse if you are sorting date fields. The default sort turns a Date value into words, like:
"Sun Nov 1..." which will be sorted after
"Mon Nov 2..."
But that's just the default behavior. Array.sort() lets you customize the action by specifying a function that it will use as it sorts. In the next example, we provide a comparison function so that the sort will work for numeric values:
The comparison function gets called many times during the sorting operation. Each time, it is passed two values (v1 and v2). All you need to do is decide which value is higher and return an indicator:
Return a positive number (say, 1) if v1 is greater than v2
Return 0 if v1 and v2 are equal for sorting purposes
(Mnemonic: "I'm positive it's greater!")
You will often see a shortcut for numeric values -- if you subtract v2 from v1, the result is positive or negative or zero, exactly as desired for an ascending numeric sort. It's also possible to create an anonymous function right in the sort call itself. Here is an example of those two concepts combined:
Sorting Descending vs. Ascending
Sorting in reverse order (Z..A or 9...0) is (or at least, should be) easy. In your comparison function, just reverse the logic used in normal (A..Z) sorting. But I have to admit that I get confused... was I supposed to subtract v1 from v2, or am I supposed to pretend that v1 is lower, to make it go higher in the list, or... all of these musings just make it hard to remember what to do. However... There is a very easy trick:
Sorting Dates
As mentioned earlier, if your array data consists of date values, the normal (default) handling of Array.sort() will fail disastrously. So, if you sort on a date field, you must supply a comparison function. Assuming that the array items are actual Date datatype values, you can use normal comparison logic -- JavaScript's < and > operators work perfectly for Date object values:
Another commonly-used option: Avoid storing Date objects in the array, and instead, store text strings that are in an implicitly sortable format: YYYY-MM-DD
The trick with this technique is to be sure that all of the dates are formatted correctly with four-digit years, and two digits (including leading zero if needed) for the month and the day.
Sorting Collections (Records)
As we discussed in the last installment, it is a common programming task to work with an array of objects, in which each object is a record, containing several fields. The usual scenario is to display such data in a table, and allow the user to be able to sort it various ways. We'll look at how to sort by any single field, then reverse the order (sort descending) on a single field, and finally, we'll see how to sort on two fields -- a primary and a secondary.
Our data record will be a collection of information about articles at Experts Exchange. Here is the "defining" structure (the constructor function that we'll use as a structure definition):
Note the addition of some program logic (lines 8-10) that generates an integer value based on the award level. This constructor logic saves some steps later when we want to sort the record array.
Here is our sample dataset, defined in source code (it would typically be read from a web page or a database):
I wrote a little "display the data" function. Here is the table before and after using the default sort:
Look closely -- the default sort seems to randomize the list! That's because without a comparison function, each array item is converted to the text "[Object object]" for comparison purposes, and that's not useful at all.
Here's a simple version of a comparison function for our array of records:
And the result is somewhat better:
However, we'd probably prefer to see the sort that ignored character case so that, for instance, "angleIII" is at the top of the list. Here's a variation that does that:
And the result is exactly what we want:
But that causes a problem if we want to sort of a numeric field -- the toLowerCase() function expects a string, not a number and using it on a numeric value will cause an error. Here's how to handle that: Add logic in the comparison function that checks the data type being compared...
This also works for sorting on nAwardLvl since it, too, has a numeric value.
It's more likely that a person would want to see the articles sorted with the "best" at the top. The nAwardLvl field has been set up so that higher values mean higher accolades. So if we were to sort by descending values, then we'd get what we want. That means just inserting the swapping logic we covered earlier, like so:
Note that "EEA,CP" is considered equal to "EEA-only" by our simplistic evaluator in the constructor (both cause nAwardLvl to be set to 2). I'll leave it as an exercise to the student to rewrite the constructor so that when sorted by nAwardLvl, "EEA,CP" will always be "higher" than "EEA-only."
Sorting on Two Fields
When there are many records and when multiple records might have the same value, you'll want to provide a means for the user to do a secondary-field sort. For instance, the telephone book has lots of entries for people with the surname "Jones" in displaying that list, you most certainly want to do a secondary sort on the first name.
One way to handle that is to have your comparison function synthesize a combined value; for instance:
Though simple, that's not very flexible; for one thing, it fails for numeric sorts. To create a generalized solution, just think about this: The secondary sort comes into play only when the two primary sort keys are equal. So all you need to do is add some logic to the comparison function where you would normally return 0. Here's a simplified example of what I mean:
So, here's our output when sorted by Author (one key) and then when sorted by Author and Title (two keys).
To sort by three keys, just add another comparison sequence when the two secondary keys are equal.
At long last, here's the "final" code that includes:
- String sort that ignores character case
- Numeric sort that works as expected (e.g., 99 < 100)
- Sort Ascending (AtoZ) or descending (ZtoA)
- Ability to specify which field to use as the sort key
- Ability to specify a secondary sort
- Uses "local" object attributes of the Array object to avoid global variables
And here is the complete code of an HTA (Hypertext Application) that uses these JavaScript techniques.
=-=-=-=-=-=-=-=-=-=-=-=-=-
If you liked this article and want to see more from this author, please click the Yes button near the:
Was this article helpful?
label that is just below and to the right of this text. Thanks!
=-=-=-=-=-=-=-=-=-=-=-=-=-