21

I have tried this code

function sort() {
    var ary = [2, 1, 0.4, 2, 0.4, 0.2, 1.5, 1, 1.1, 1.3, 1.2, 0.2, 0.4, 0.9];
    alert(ary.sort(function(a, b) {return a < b;}));
}
sort();

but the result is

[1, 2, 2, 1.1, 0.9, 1.2, 1.5, 1, 1.3, 0.4, 0.4, 0.4, 0.2, 0.2]

It works if length of array is short. But it doesn't work for long array. Thanks.

namgold
  • 1,009
  • 1
  • 11
  • 32
Fresh Friend
  • 489
  • 1
  • 3
  • 11

4 Answers4

39

Edited with additional info:

My apologies, but the shortest answer for this question is just:

function sort() {
    var ary = [2, 1, 0.4, 2, 0.4, 0.2, 1.5, 1, 1.1, 1.3, 1.2, 0.2, 0.4, 0.9];
    // use custom compare function that sorts numbers ascending
    alert(ary.sort(function(a, b) {
        return a - b;
    }));
}

sort();

Note that if a compare function is not supplied to the sort method, elements are sorted by converting them to strings and comparing strings in Unicode code point order. So [1, 2, 10].sort() produces [1, 10, 2] because "10", as a string, comes before "2". The code above will return the array sorted from smallest to largest correctly.

You can sort largest to smallest (descending order) by reversing a and b within the return statement:

function (a, b) {
    return b - a;
}
Marcus Parsons
  • 1,714
  • 13
  • 19
  • 2
    This is wrong. Arrays would be sorated as strings by default. – Salman A Jan 03 '18 at 17:28
  • 1
    A quick counterexample: `[9, 99, 901].sort()` returns `[9, 901, 99]`, which is clearly not in numerical order. – CRice Jan 03 '18 at 17:33
  • 2
    Post edited and updated with additional information as to why this happens, too. Thanks. – Marcus Parsons Jan 03 '18 at 17:35
  • sorry, it is Still wrong about _default_ behavior. – Salman A Jan 03 '18 at 17:39
  • 4
    Please let me know where I am wrong. But quoting directly from the [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort) article for the `sort` method: "If compareFunction is not supplied, elements are sorted by converting them to strings and comparing strings in Unicode code point order." – Marcus Parsons Jan 03 '18 at 17:50
28

You sorting is failing because your comparison function does not meet the specifications for Array.sort:

  • If compareFunction(a, b) is less than 0, sort a to an index lower than b, i.e. a comes first.
  • If compareFunction(a, b) returns 0, leave a and b unchanged with respect to each other, but sorted with respect to all different elements. Note: the ECMAscript standard does not guarantee this behaviour, and thus not all browsers (e.g. Mozilla versions dating back to at least 2003) respect this.
  • If compareFunction(a, b) is greater than 0, sort b to an index lower than a, i.e. b comes first.
  • compareFunction(a, b) must always return the same value when given a specific pair of elements a and b as its two arguments. If inconsistent results are returned then the sort order is undefined.

Your comparison function returns a boolean, which is effectively only returning the values 0 and 1. You should fix your comparison function according to the spec like in David's answer. Here's a simple comparison function1:

var ary = [2, 1, 0.4, 2, 0.4, 0.2, 1.5, 1, 1.1, 1.3, 1.2, 0.2, 0.4, 0.9];
console.log(ary.sort(compareDecimals));

function compareDecimals(a, b) {
    if (a === b) 
         return 0;

    return a < b ? -1 : 1;
}

The other answers (of using function { return a - b; } take advantage of mathematical coincidence. Namely that equal values have a difference of 0. This works for "normal" values, but it's prone to errors when your data contains values like Inifinity or Number.MIN_SAFE_INTEGER.


1. As noted in the comments, this function does not address all of the crazy javascript number behavior, for example that NaN === NaN evaluates to false. Likewise for dealing with mixed-type arrays. Engineer your comparison function as needed depending on the nature of your data.

ryanyuyu
  • 6,366
  • 10
  • 48
  • 53
  • The first example can also fail, for NaN, which is a number. Just saying. – Salman A Jan 03 '18 at 17:36
  • @SalmanA thanks. I've noted this in a footnote since it's not really the focus of the core issue, which is about how `array.sort` actually works. – ryanyuyu Jan 03 '18 at 17:36
  • @ryanyuyu, thank you for bringing this up. `Infinity` and `Number.MIN_SAFE_INTEGER` do work with the default sorting method. `Infinity` is represented in memory as a value along with `Number.MIN_SAFE_VALUE` so if you add those into the array (using the default behavior), they will sort accordingly. – Marcus Parsons Jan 03 '18 at 17:43
  • Let me also say that NaN works with the default function of sort (always at the end of the array), however in this comparison function, NaN will cause the sorting to go awry. The output from this: var ary = [2, 1, 0.4, 2, 0.4, 0.2, 1.5, 1, 1.1, 1.3, 1.2, 0.2, 0.4, 0.9, Infinity, Number.MIN_SAFE_INTEGER, NaN]; console.log(ary.sort(compareDecimals)); function compareDecimals(a, b) { if (a === b) return 0; return a < b ? -1 : 1; } is: [1.1, NaN, -9007199254740991, 0.2, 0.2, 0.4, 0.4, 0.4, 0.9, 1, 1, 1.2, 1.3, 1.5, 2, 2, Infinity] – Marcus Parsons Jan 03 '18 at 17:45
  • Also, consider the fact that JS math is not precise. See https://stackoverflow.com/questions/5153061/simple-subtraction-bug-in-javascript – Matthias Dec 13 '18 at 22:12
4

Try:

var ary = [2, 1, 0.4, 2, 0.4, 0.2, 1.5, 1, 1.1, 1.3, 1.2, 0.2, 0.4, 0.9];

function compare(a, b) {
    if (a < b) {
        return -1;
    } else if (a > b) {
        return 1;
    } else {
        return 0;
    }
}


ary = ary.sort(compare);
alert(ary);
David Anthony Acosta
  • 4,766
  • 1
  • 19
  • 19
3

Your code has a typo in alert.

Anyway the correct implementation is -

function sort() {
        var ary = [2, 1, 0.4, 2, 0.4, 0.2, 1.5, 1, 1.1, 1.3, 1.2, 0.2, 0.4, 0.9];
        return ary.sort(function(a, b) {return a - b;});
}
    
alert(sort());

outputs - [0.2, 0.2, 0.4, 0.4, 0.4, 0.9, 1, 1, 1.1, 1.2, 1.3, 1.5, 2, 2]

(use b - a to change the sort order).

planet_hunter
  • 3,866
  • 1
  • 26
  • 39