[9,10,1].sort()の結果が[1,10,9]になってしまう。バカなの?
問題
[9,10,1].sort()の結果が[1,10,9]になってしまう。もちろん期待値は[1,9,10]だ。
console.log([9,10,1].sort()) // [1,10,9]
お前はソートもできないのか? 使えねー
解決
ソート順を決める関数を引数に渡す。
console.log([9,10,1].sort((a, b) => a - b)) // [1,9,10]
引数を次のようにすることでソート方法を変更できる。
| ソート | (a,b)=>{} |
|---|---|
| 昇順 | (a, b) => a - b) |
| 降順 | (a, b) => b - a) |
面倒くさっ こんなの覚えられないよ……
原因
Array.sort()によると仕様である。
ソート順は昇順で、要素を文字列に変換してから、 UTF-16 コード単位の値の並びとして比較します。
数値でソートして欲しかったのに、文字列に変換しやがる仕様のせいで今回のバグが起きた。
なんでや!
なぜこんな仕様になっているの?
[9,10,1].sort()は見てのとおり数値の配列だ。直感的にみて、数値順でソートしてほしい。なのに、なぜ文字列順でソートする仕様になっているのか?
原因は型がないせいだと思う。JavaScriptの言語仕様はタイプセーフじゃない。配列に入れる値の型は数値型であったり文字列型であったり、それらを混在させることができてしまう。たとえば[1,'A'].sort()など。それらをソートするなら、すべての型に共通するtoString()メソッドを使って文字列化してソートするしかない。だから文字列順ソートなのだと思う。
C#など他の型があるタイプセーフなプログラミング言語なら、List<int>のようにジェネリクスで配列要素の型を限定できる。これによりソートアルゴリズムもその型にあわせたものになってくれる。数値型なら数値順でソートする。私はそれを期待していた。でもJavaScriptはタイプセーフ言語じゃないから文字列ソートになってしまう。
こんなひどい罠があるとは……。
やっぱJavaScriptってクソだわ