'The ArraySort callback is not sorting my array in the correct order
Using ColdFusion to sort an multi-dimensional array based on a "Price per sq. ft" field from high to low.
It has been in production and worked in testing, but a case has come up that has yielded strange, unsorted results. I also ran this on the CFDOCS site using their ArraySort
code and got the same, incorrect sort results.
As you can see the results are not even really sorted.
Here's my code:
figures = [
{name='carl',price='117.5'},
{name='fen',price='116.4'},
{name='joe',price='86.3'}
];
arraySort(figures, function (a, b){
return compare(b.price, a.price);
});
writeDump(figures);
Results:
NAME joe
PRICE 86.3
NAME carl
PRICE 117.5
NAME fen
PRICE 116.4
It should be sorted in this order: 117.5, 116.4, 86.3.
I believe it's sorting in a way that makes 86.3
appear greater than the rest because it starts with an 8? I also tried without the quotes and got the wrong results as well.
I ran this same code on cfdocs.org and got the same, wrong results.
Am I doing something incorrectly in my sort parameters or on the callback?
Thank you!
Solution 1:[1]
The ArraySort callback function "compares two elements of the array" at a time, and should return one of the following values:
-1
if the first element is less than the second0
if the first element is equal to the second1
if first element is greater than the second
While the compare() function does return 1
,0
or -1
, it compares the elements as strings, which isn't going to produce the expected order for numeric values. As Shawn suggested, adding some debugging code will show the results of each comparison:
arraySort(figures, function (a, b){
local.num = compare(a.price, b.price);
local.text = local.num == -1 ? " less than " : (local.num == 0 ? " equals " : " greater than");
writeOutput("<br> "& a.price &" "& local.text &" "& b.price &" => "& local.num );
return local.num ;
});
.. demonstrating that a string comparison doesn't produce the same results as a numeric comparison:
- 116.4 less than 117.5 => -1
- 86.3 greater than 116.4 => 1
- 86.3 greater than 117.5 => 1
- 1117.3 less than 117.5 => -1
- 1117.3 less than 116.4 => -1
To sort the "price" values as numbers, use arithmetic operators instead of compare(). For descending order (high to low):
arraySort(figures, function (a, b){
return (b.price < a.price) ? -1 : (b.price == a.price ? 0 : 1);
});
For ascending order (low to high), just swap the comparisons:
arraySort(figures, function (a, b){
return (a.price < b.price) ? -1 : (a.price == b.price ? 0 : 1);
});
Solution 2:[2]
As @SOS mentioned, it appears the compare()
function compares numbers as strings even if you wrap them in a val()
. But where he uses a ternary, you can also use the sgn()
function instead.
For example...
// ascending order
arraySort(figures, function (a, b){
return sgn(a.price - b.price);
});
// .. or for descending order
arraySort(figures, function (a, b){
return sgn(b.price - a.price);
});
The sgn()
function will always return a 1 for positive numbers, -1 for negative numbers, and 0 for zero...making it the perfect function (IMHO) for this use case.
Solution 3:[3]
Consider the compare()
function. It can return -1, 0, or 1. Only 0 is considered false. To correct this you need
arraySort(figures, function (a, b){
return compare(b.price, a.price) == 1 ? 1 : 0;
});
writeDump(figures);
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 | SOS |
Solution 3 | James A Mohler |