JSで数値をソートすると文字列順になる罠
[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ってクソだわ