
The Curious Case of JavaScript’s `sort` - hoov
https://influxdata.com/blog/the-chronograf-files-the-curious-case-of-javascripts-sort/
======
wscott
Yes yes, everyone one is saying "of course" these are string compares! What a
nub mistake. But is it?

It is a very easy mistake to make, you pick 'sort' and the resulting code does
what you expect. As long as all the timet's have the same number of digits
then the string comparison works. You have to have a data that crosses
9/8/2001 to see the problem. (or 3/3/1973 before that)

The javascript compiler isn't going to tell you about this and it only hits a
problem with dates older than most programs that are written in javascript.

~~~
fenomas
Certainly it's an easy mistake to make, but most JS devs tend to encounter it
the very first time they call sort() on anything that isn't epoch dates. The
fact that the author's code worked for years is a pretty exotic edge case!

------
meesles
Sounds like your solution would have been to properly read documentation of
critical sectors of your code. Assuming that just calling `.sort()` on your
arbitrary data would give you exactly what you want was a mistake. Not testing
this code was also a mistake, because any combination of different length
integers would have shown you this problem very early on, saving you some
embarrassment and loss of user trust.

~~~
eximius
I would definitely expect calling `sort` on plain old integers to work exactly
how I want.

That said, they probably should have read the documentation as I doubt that
was the only use case.

------
gpvos
Really, their testing was inadequate. If your product is a timeline graph, you
should test it with data covering a large range across the possible time
values. A single date from before roughly 2000 would have uncovered the
problem.

(Not that I always test adequately, mind you.)

------
SimeVidas
Why is scrolling so slow in Firefox on that page?

~~~
gpvos
They do something to the scroll events on that page. It's pretty terrible.

------
Domenic_S
MySQL exhibits similar behavior if you're comparing numbers stored in a field
with string-ish type (eg, varchar):

    
    
      mysql> select cast(123 as char(255)) > "2";
      +------------------------------+
      | cast(123 as char(255)) > "2" |
      +------------------------------+
      |                            0 |
      +------------------------------+
      1 row in set (0.00 sec)
    

It can really bite you if you're filtering queries on "WHERE char_field > 10".
I personally did not think a lot about datatypes after initial table
creation... until I ran into this.

------
simlevesque
Stop hijacking my scrollwheel.

------
witty_username
So if I'm understanding correctly, JS sort sorts numbers by their string
value? That's crazy. And it seems you have to add a compare function just to
do a basic operation to sort numbers.

~~~
bilalq
No, this has nothing to do with how "JS sorts numbers". JavaScript sorts
_arrays_ by using a default comparator that runs .toString and compares
unicode code point values (with safe handling around null/undefined).

There is no concept of a typed number array in JS. There is just "Array", and
an array's contents can be heterogenous.

Imagine the following:

    
    
        ['a', 'c', 'b'].sort() // ['a','b','c']
        ['a', 1].sort() // [1, 'a']
        [1, ['foo'], {bar: 'baz'}, 'a', '2', null, undefined].sort() // [1, '2', {bar:'baz'}, 'a', ['foo'], null, undefined]
    
    

The default comparator is one that can be run on any arbitrary array. It's up
to you as a caller to specify that you'd like to sort by a different
comparator.

For a more detailed read, see: [https://developer.mozilla.org/en-
US/docs/Web/JavaScript/Refe...](https://developer.mozilla.org/en-
US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort)

~~~
zaphar
And this kids is an example of a problem that compile time type checking can
help with.

~~~
toupeira
Or simply strong typing.

------
apo
Also see:

[http://blog.rodneyrehm.de/archives/14-Sorting-Were-Doing-
It-...](http://blog.rodneyrehm.de/archives/14-Sorting-Were-Doing-It-
Wrong.html)

------
spiderfarmer
You should have read W3Schools:

> However, if numbers are sorted as strings, "25" is bigger than "100",
> because "2" is bigger than "1".

~~~
andreapaiola
Ah ah nope!

You should read MDN

The default sort order is according to string Unicode code points.

If compareFunction is not supplied, elements are sorted by converting them to
strings and comparing strings in Unicode code point order. For example,
"Banana" comes before "cherry". In a numeric sort, 9 comes before 80, but
because numbers are converted to strings, "80" comes before "9" in Unicode
order.

[https://developer.mozilla.org/en-
US/docs/Web/JavaScript/Refe...](https://developer.mozilla.org/en-
US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort)

~~~
Klathmon
And just to throw another wrench in there, because it sorts by Unicode code
points, capitals come before lowercase.

So this:

['Banana', 'cherry', 'bagel', 'apple'].sort()

Will produce this order:

["Banana", "apple", "bagel", "cherry"]

------
sriram_iyengar
have seen people raising big uffs-oohs-aahs because of stackoverflow-driven-
development :)

developer mozilla helps [https://developer.mozilla.org/en-
US/docs/Web/JavaScript/Refe...](https://developer.mozilla.org/en-
US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort)

unit tests help !

------
js2
The suggestion:

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

Can this not suffer from overflow/underflow?

~~~
gpvos
Javascript numbers are reals, not integers. If they become too large
(positively or negatively), they become +Infinity or -Infinity.

------
gingerrr
ITT: people who expect a language to work just like they want it to instead of
reading the docs for the language.

------
cristiantincu
Spec:

1\. [http://www.ecma-international.org/ecma-262/6.0/#sec-
array.pr...](http://www.ecma-international.org/ecma-262/6.0/#sec-
array.prototype.sort)

2\. [http://www.ecma-international.org/ecma-262/6.0/#sec-
sortcomp...](http://www.ecma-international.org/ecma-262/6.0/#sec-sortcompare)

------
CJefferson
I had some javascript where I wanted to sort a list of list of integers as
follows:

    
    
        * Sort each inner list smallest to largest
        * Sort the list of lists lexicographically
    

It amazed that (a) how stupid javascript's default sort is, and (b) how none
of the famous libraries (underscore/lodash) seem to have even fixed the
problem of arrays not being compared element-wise, or provide an easy drop-in
replacement.

EDIT: I replaced the word 'lexicographic' with 'element-wise', as I think it
might be causing confusion.

~~~
akavi
What would "lexicographic" comparison of two arrays mean? As in, what's the
canonical string conversion, and how should commas be treated in the ordering?
(eg, does `[1,2]` come before or after `[12]` ?)

Admittedly, "lexicographic" sorting of numbers is pretty wonky too, but at
least there's a pretty canonical string representation of a given number.

It seems like a pretty idiosyncratic need without an obvious canonical
interpretation, so I'm a little surprised by your surprise at there not being
standardish library function for it.

Edit: Oh, how embarrassing, I misunderstood the linked post (I thought it was
about JS lexicographically sorting integers, rather than arrays of integers).
Well, I guess my surprise applies to both you and the author of the link.

~~~
CJefferson
Lexicographic ordering of two arrays X and Y is (I think) the standard
ordering of arrays. Algorithm something like (pseudocode, ignoring different
length arrays):

    
    
        for(i = 0; i < length(X); ++i) {
           if(X[i] < Y[i]) X is smaller
           if(X[i] > Y[i]) Y is smaller
        }
        arrays are equal!
    

In every programming language I've ever used (other than Javascript), the
following is how arrays are ordered (well, except for languages like C, where
they are compared by memory location by default)

[1,1] < [2,2] < [11,11] < [22,22]

Instead Java says [1,1] < [11,11] < [2,2] < [22,22], because it compares the
arrays as strings.

