The Curious Case of JavaScript’s `sort`
influxdata.com
influxdata.com
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.
That said, they probably should have read the documentation as I doubt that was the only use case.
(Not that I always test adequately, mind you.)
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.What is the correct order of the items ["2", 10, "banana", [[]], "", 1]?
See also https://www.destroyallsoftware.com/talks/wat
Bonus: sometimes you can get the same kind of behaviour in Excel when importing CSV.
It is trickier when someone compares "2" and 3, should we say it's invalid (my preference) or turn 2 into an integer, or 3 into a string? However, there is NO reason for there to be an issue comparing 1 and 11. This is just an unfortunately mistake that is too deeply baked into the language to fix at this point.
> [2] < [11]
false
Technically, a combination of integers and arrays. Still took me an afternoon to track down (it didn't occur to me javascript would work like that).Obviously it's too late to change JavaScript now, but Iwould be interested to know why it would have been complicated to do this differently originally, and why it would be countintuitive? Is there anything I can read about this kind of thing?
I guess "Javascript: the Good Parts" is as good a place to start as any. It doesn't really go into the design decisions though.
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...
Note that nowadays there are indeed Int16Array, Float32Array, and so forth, and their sort methods work numerically like you'd expect.
Is there an explanation somewhere of how they decided on that as the default comparator? My first inclination would have been to use "<" as the default comparator. That would sort purely numeric arrays numerically, and purely string arrays lexically, leaving only the case of arrays that mix numerical and non-numerical elements needing the programmer to step in and give an explicit comparator.
It may seem like a strange way to spec the language, but I'm not sure what would have been better. It wouldn't make sense for sort to have to run through the array checking types before it starts, or for it to dynamically change what kind of comparison it does depending on the types of each pair of elements being compared.
arr1.sort( Array.NUMERICAL | Array.DESCENDING )
arr2.sort( Array.CASE_INSENSITIVE )
or similar.E.g.:
[1,3,NaN,2,5].sort( (a,b) => a<b ? -1 : 1 )
// Chrome: [ NaN, 1, 2, 3, 5 ]
// Firefox: [ 2, 5, NaN, 1, 3 ]In (almost) every other language ever the following is true. In Javascript it is false.
[2] < [11]
This means:[[2], [11]].sort() // Javascript: [ [11], [2] ]
As for the actual behavior of ([2] < [11]), the issue there is that "Array" is not a type in JS, arrays are just objects that happen to have properties called "0" and "1" and so on. Hence operators don't have any special cases for arrays, so getting comparisons to work like you had hoped would mean the comparison operator would need weird special rules where, whenever it's passed two objects it checks if they both have properties called "0", and if so do one thing, if not do something else, etc.
It's JavaScript … it doesn't have to be sane; we'll use it anyway.
I think that Erlang had the most elegant solution: it defines a total order over all objects (number < atom < reference < fun < port < pid < tuple < list < bit string). Of course, Erlang's 'strings' are pretty inelegant (they're just lists of integers, which are printed as strings if they happen to incidentally be valid characters), so I guess there's some Law of Conservation of Bogosity at play here.
> However, if numbers are sorted as strings, "25" is bigger than "100", because "2" is bigger than "1".
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...
So this:
['Banana', 'cherry', 'bagel', 'apple'].sort()
Will produce this order:
["Banana", "apple", "bagel", "cherry"]
developer mozilla helps https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
unit tests help !
function compareNumbers(a, b) {
return a - b;
}
Can this not suffer from overflow/underflow? * 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.
var x : number[] // should compile: x.sort(function(a,b) { return a - b; })
var y : string[] // should compile: y.sort()
The reason that this isn't done is due to a misplaced ideology about "typescript is just javascript" - or some similar nonsense.
The bigger problem here is that we are using tools that are designed by a committee, and we have almost no hope of getting this fixed.
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.
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.