読者です 読者をやめる 読者になる 読者になる

Array.prototype.sort

>>> [5,10,2,4,30].sort();
[10, 2, 30, 4, 5]

えー、なにそれー

compareFunction (比較関数) が与えられなかった場合、要素はそれぞれの文字列に変換したものを比較して辞書 (あるいは電話帳。数的でない) 順にソートされます。例えば、"80" は辞書順では "9" の前に来ますが、数的なソートでは 9 が 80 の前に来ます。

そ、そうなのか。

ECMA-262 5th EditionによるとArray.prototype.sortの比較操作は

13. If the argument comparefn is not undefined, then
  a.If IsCallable(comparefn) is false, throw a TypeError exception.
  b.Return the result of calling the [[Call]] internal method of comparefn passing undefined as the this value and with arguments x and y.
14. Let xString be ToString(x).
15. Let yString be ToString(y).
16. If xString < yString, return −1.
17. If xString > yString, return 1.
18. Return +0.

比較関数を渡さない場合は文字列として比較する仕様らしい。数値で比較したいなら比較関数を渡せと。

>>> [5,10,2,4,30].sort(function(a,b){return a-b});
[2, 4, 5, 10, 30]

ま、JavaScriptは文字列と数値の境目にあいまいなところがあるので、文字列で比較というのは無難なのかもしれないな…

>>> ["12",10,"11",13, 9, "8",7].sort();
[10, "11", "12", 13, 7, "8", 9]

そういえば、Rubyはどうなんだろう?

irb(main):001:0> ["12",10,"11",13, 9, "8",7].sort
ArgumentError: comparison of Fixnum with String failed
        from (irb):1:in `sort'
        from (irb):1

JavaScriptほど型に甘くない。

Rubyはsort_byを使ってIntegerとして比較するかStringとして比較するか決められる。

irb(main):003:0> ["12",10,"11",13, 9, "8",7].sort_by{|i| i.to_i }
=> [7, "8", 9, 10, "11", "12", 13]

irb(main):004:0> ["12",10,"11",13, 9, "8",7].sort_by{|i| i.to_s }
=> [10, "11", "12", 13, 7, "8", 9]

ま、普通はmapとかで型をそろえるだろうな

irb(main):006:0> ["12",10,"11",13, 9, "8",7].map{|i| i.to_i }.sort
=> [7, 8, 9, 10, 11, 12, 13]

ついき

最近使ってなくてすっかり忘れてたけどperlも文字列の辞書順でソートだった。別にJavaScriptが特別変わっているというわけじゃないすね…

$ perl -e "print join(',', sort (10,2,'30',4,50))"
10,2,30,4,50

$ perl -e "print join(',', sort { $a <=> $b  }(10,2,'30',4,50))"
2,4,10,30,50