JavaScript sorts arrays weird
You don’t have to dig deep to find something strange in JavaScript. Any language created in 10 days would have quirks. While it has seen a lot of improvement in the last few years, some weirdness will always remain. One headscratcher is the default way it sorts arrays.
An array in JavaScript can hold all kinds of values. We can put whatever we want in them: numbers, strings, objects, other arrays, or anything else. We can also put all of them in the same array. Just because there are five numbers in an array does not prevent us from putting a string in there as well.
// this is totally fine
[15, true, "Unobtanium", [2, 4], null, 81]
// this is totally fine
[15, true, "Unobtanium", [2, 4], null, 81]
// this is totally fine
[15, true, "Unobtanium", [2, 4], null, 81]
// this is totally fine
[15, true, "Unobtanium", [2, 4], null, 81]
To sort arrays, we can use the built-in .sort() function. If we call it without any parameters, JavaScript applies a default sorting. It sorts an array of strings alphabetically:
["cucumber", "apple", "banana"].sort()
// ⇒ ["apple", "banana", "cucumber"]
["cucumber", "apple", "banana"].sort()
// ⇒ ["apple", "banana", "cucumber"]
["cucumber", "apple", "banana"].sort()
// ⇒ ["apple", "banana", "cucumber"]
["cucumber", "apple", "banana"].sort()
// ⇒ ["apple", "banana", "cucumber"]
This is pretty straightforward so far. Check out how it sorts the numbers 1 through 10:
[3, 5, 6, 8, 10, 7, 2, 1, 9, 4].sort()
// ⇒ [1, 10, 2, 3, 4, 5, 6, 7, 8, 9]
[3, 5, 6, 8, 10, 7, 2, 1, 9, 4].sort()
// ⇒ [1, 10, 2, 3, 4, 5, 6, 7, 8, 9]
[3, 5, 6, 8, 10, 7, 2, 1, 9, 4].sort()
// ⇒ [1, 10, 2, 3, 4, 5, 6, 7, 8, 9]
[3, 5, 6, 8, 10, 7, 2, 1, 9, 4].sort()
// ⇒ [1, 10, 2, 3, 4, 5, 6, 7, 8, 9]
It seems like JavaScript thinks 10 is larger than 1 but smaller than 2, which would be bonkers. What is going on here is something else. Because it does not try to understand what values those are, it treats ALL values as strings when sorting. Because alphabetically, the “1” in 10 comes before the “2” in 2, it puts 10 before 2. It does the same with other values (except undefined
which always ends up last):
["trauma", null, "why", true, undefined, false].sort()
// ⇒ [false, null, "trauma", true, "why", undefined]
["trauma", null, "why", true, undefined, false].sort()
// ⇒ [false, null, "trauma", true, "why", undefined]
["trauma", null, "why", true, undefined, false].sort()
// ⇒ [false, null, "trauma", true, "why", undefined]
["trauma", null, "why", true, undefined, false].sort()
// ⇒ [false, null, "trauma", true, "why", undefined]
The sorted version doesn’t seem any more or less sorted than the original version. JavaScript tries its best here by falling back to sorting alphabetically. It’s not obvious what order would make most sense in this last example.
We can help JavaScript out by providing a function to sort by. For numbers, we can do this in a single line:
[3, 5, 6, 8, 10, 7, 2, 1, 9, 4].sort((a, b) => a - b)
// ⇒ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[3, 5, 6, 8, 10, 7, 2, 1, 9, 4].sort((a, b) => a - b)
// ⇒ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[3, 5, 6, 8, 10, 7, 2, 1, 9, 4].sort((a, b) => a - b)
// ⇒ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[3, 5, 6, 8, 10, 7, 2, 1, 9, 4].sort((a, b) => a - b)
// ⇒ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
To sort numbers from largest to smallest, we swap a
and b
:
[3, 5, 6, 8, 10, 7, 2, 1, 9, 4].sort((a, b) => b - a)
// ⇒ [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
[3, 5, 6, 8, 10, 7, 2, 1, 9, 4].sort((a, b) => b - a)
// ⇒ [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
[3, 5, 6, 8, 10, 7, 2, 1, 9, 4].sort((a, b) => b - a)
// ⇒ [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
[3, 5, 6, 8, 10, 7, 2, 1, 9, 4].sort((a, b) => b - a)
// ⇒ [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
For most other cases, the correct sorting will depend on your data and the domain you work in. MDN has another few examples to check out: