Recipe 6.11 Implementing a Custom Sort
6.11.1 Problem
You want to sort an array
in a way such that the basic
sort( ) and sortOn( )
methods do not suffice. You want to sort an array in a
case-insensitive manner, perform a numeric sort, or use another
custom or multikey criterion.
6.11.2 Solution
Use the sort( ) method and pass it a reference
to a compare function.
6.11.3 Discussion
If you want complete control
over sorting criteria, use the
sort( ) method with a custom compare
function (also called a sorter
function). The compare function is called repeatedly by
the sort( ) method to reorder two elements of
the array at a time. The compare function receives two parameters
(let's call them a and
b), which it should compare to determine which
one should be ordered first. Your custom compare function should
return a positive number, a negative number, or 0, depending on how
the elements are to be sorted. If the function returns a negative
number, a is ordered before
b. If the function returns 0, then the current
order is preserved. If the function returns a positive number,
a is ordered after b. Your
compare function is called with every relevant combination of
elements until the entire array has been properly ordered. Using a
custom compare function is easier than it sounds. You do not need to
concern yourself with the details of the sorting algorithm; you
simply specify the criteria for comparing any two elements.
Here is a simple compare function that performs a case-insensitive
sort:
function insensitiveSorter(a, b) {
itemOne = a.toUpperCase( );
itemTwo = b.toUpperCase( );
if (itemOne > itemTwo) {
return 1;
} else if (itemOne < itemTwo) {
return -1;
} else {
return 0
}
}
Case-insensitive
sorting is useful when you have an array of values with mixed cases,
because Flash automatically sorts all uppercase letters before
lowercase letters by default:
myArray = ["cardinal", "California", "camel", "Chicago"];
myArray.sort( );
trace(myArray); // Displays: California,Chicago,camel,cardinal
However, when you use the case-insensitive sort utilizing the custom
sorter function as defined previously, Flash sorts the values in
alphabetical order regardless of case:
myArray = ["cardinal", "California", "camel", "Chicago"];
myArray.sort(insensitiveSorter);
trace(myArray); // Displays: California,camel,cardinal,Chicago
When sorting numbers, the standard sort( )
method produces unexpected results. Numbers are sorted
"alphabetically" instead of
numerically. After the following example, myArray
is [1,12,2,3,4,43], not [1,2,3,4,12,43]:
myArray = [1, 12, 2, 3, 43, 4];
myArray.sort( )
trace (myArray); // Displays: 1,12,2,3,4,43 not 1,2,3,4,12,43
Here is a simple compare function that performs a numeric sort,
instead of a string-based sort, even if the elements are strings,
such as "1",
"2",
"3":
function numberSorter(a, b) {
itemOne = parseInt(a);
itemTwo = parseInt(b);
if (itemOne > itemTwo) {
return 1;
} else if (itemOne < itemTwo) {
return -1;
} else {
return 0
}
}
You can use it as follows:
myArray = [1, 12, 2, 3, 43, 4];
myArray.sort(numberSorter);
trace (myArray); // Displays: 1,2,3,4,12,43
In the numberSorter( ) function, the difference
between the two numbers yields a positive result if
a is greater than b, and a
negative result if the opposite is true. It yields 0 if they are
equal. Therefore, numeric comparisons of the previous type can be
simplified as follows.
function numberSorter(a, b) {
itemOne = parseInt(a);
itemTwo = parseInt(b);
return (itemOne - itemTwo);
}
You can easily modify the sort function to sort the numbers in
reverse order, as follows:
function reverseSorter(a, b) {
itemOne = parseInt(a);
itemTwo = parseInt(b);
return (itemTwo - itemOne);
}
Here is a full-fledged example that sorts the cars
array by make and year:
// Create an array with elements that have some matching make properties but
// different year and color properties.
cars = new Array( );
cars.push({make: "Honda", year: 1997, color: "maroon"});
cars.push({make: "Chrysler", year: 2000, color: "beige"});
cars.push({make: "Mercedes", year: 1985, color: "blue"});
cars.push({make: "Fiat", year: 1983, color: "gray"});
cars.push({make: "Honda", year: 1982, color: "white"});
cars.push({make: "Chrysler", year: 1999, color: "green"});
cars.push({make: "Mercedes", year: 2002, color: "tan"});
cars.push({make: "Fiat", year: 1981, color: "brown"});
// Create the custom compare function. The function is always passed two elements as
// parameters. It is convenient to call them a and b.
function sorter(a, b) {
// If the make property of a is larger than (meaning it comes alphabetically after)
// the make property of b, return 1 to sort a after b. If the make property of a is
// less than the make property of b, then return -1 to sort a before b. Otherwise
// (if a.make and b.make are the same), perform the comparison on the year
// property. We use String.toUpperCase( ) to ensure a case-insensitive comparison.
// We also convert the year to an integer and use the aforementioned shortcut for
// numeric comparison.
makeOne = a.make.toUpperCase( );
makeTwo = b.make.toUpperCase( );
if (makeOne > makeTwo ) {
return 1;
} else if (makeOne < makeTwo) {
return -1;
} else {
return (parseInt(a.year) - parseInt(b.year))
}
}
// Call the sort( ) method and pass it a reference to the sorter( ) compare function.
cars.sort(sorter);
// Loop through the array and output the results.
for (var i = 0; i < cars.length; i++) {
// Displays the results alphabetically by make, then by year:
// A green 1999 Chrysler
// A beige 2000 Chrysler
// A brown 1981 Fiat
// A gray 1983 Fiat
// A white 1982 Honda
// A maroon 1997 Honda
// A blue 1985 Mercedes
// A tan 2002 Mercedes
trace("A " + cars[i].color + " " + cars[i].year + " " + cars[i].make);
}
|