🙌

【JavaScript】数値のみを含む配列を sort() メソッドで並び替えるときは sort() メソッドの引数に比較関数を与える

2024/03/24に公開

※この記事はQiitaの本記事にて投稿した記事に加筆・修正し、移行した記事です。

負の数を含んだ数値の配列を昇順に並び替えるときに少しハマったので、解決策を残しておきます。

要約

  • JavaScript における sort() メソッドは配列の中身を一度文字列に変換し、辞書順に順序づけをする。
  • 数値のみの配列を並び替えるときは sort() メソッドに引数として比較関数を与える。

何にハマったのか

例えばこんな配列があったとして、

sortNumbers.js
var ary = [1, -1, 5, -10, -11];

こう並び替えたいとします。

sortNumbers.js
var ary = [-11, -10, -1 , 1, 5];

要は昇順に並び替えたいということです。
しかし、 sort() メソッドを用いて配列の並び替えを試みたところ、

sortNumbers.js
var ary = [1, -1, 5, -10, -11];
ary.sort()
// => [ -1, -10, -11, 1, 5 ]

昇順に並び替えられませんでした...

なぜ昇順に並び替えられなかったのか?

何故だと考えつつ、MDN web docs の sort() メソッドに関するページを見てみたらこんなことが書いてありました。

配列の要素をin placeでソートします。このソートは stable ではありません(訳注:同じ序列を持つ値の順番が保証されません)。 デフォルトではUnicodeコードポイントの昇順にソートされます。
MDN web docs mozilla : Array.prototype.sort()より引用

つまり、sort() メソッドは、配列の中の要素を一つずつ文字列に変換してから辞書的な順番で並び替えをするということでした。
ということで、先述したように数値の大きさ順にならないことに納得はできます。
では、数値の大きさ順に並び替えるにはどうしたらいいのだろうか?と考える間もなく同じページに一つの解決策が提示されていました。

引数に比較関数を入れる

以下は sort() メソッドの構文です。

array.sort(comparefunction)

上記のように、sort() メソッドは引数として、何をもって並び替えるのか、それを定義する関数を与えることができます。
先ほどはこれを省略していたため、デフォルトの仕組みである文字列変換からの辞書順ソートが適用されていたんですね。
以下に、比較関数についての説明を引用しておきます。

compareFunction (比較関数) が与えられなかった場合、要素はそれぞれの文字列に変換したものを比較して辞書 (あるいは電話帳。数的でない) 順にソートされます。例えば、"80" は辞書順では "9" の前に来ますが、数的なソートでは 9 が 80 の前に来ます。
compareFunction が与えられた場合、配列の要素は比較関数の返り値に基づきソートされます。もし a と b が比較されようとしている要素の場合、
compareFunction(a, b) が 0 未満の場合、a を b より小さい添字にソートします。
compareFunction(a, b) が 0 を返す場合、a と b は互いに変更せず、他のすべての要素に対してソートします。注意: ECMAScript 標準はこの振る舞いを保証していないため、一部のブラウザ (例えば、遅くとも 2003 年以前のバージョンの Mozilla) はこれを遵守していません。
compareFunction(a, b) が 0 より大きい場合、b を a より小さい添字にソートします。
compareFunction(a, b) に与えられた引数が同じなら戻り値も同じでなければなりません。もし一貫性のない値を返した場合の挙動は未定義となります。
MDN web docs mozilla : Array.prototype.sort()より引用

つまり、a と b の差をとる関数を書いていけばいいらしい。

比較関数 compare()

コードは以下の様になります。

昇順に並び替える

sortNumbers.js
var ary = [1, -1, 5, -10, -11];
var f = function (a, b) {
    return a - b
}
ary.sort(f);
// => var ary = [-11, -10, -1 , 1, 5];

降順に並び替える

sortNumbers.js
var ary = [1, -1, 5, -10, -11];
var f = function (a, b) {
    return b - a
}
ary.sort(f);
// => var ary = [5, 1, -1 , -10, -11];

理屈

昇順にしたい場合、

array = [a, b, c, d]

のとき、a,b, b,c, c,d を比較していく。つまり、

array = [2, 3, 4, 1]

のとき、

array = [2, 3, 4, 1] 
・2,3 を比較
=> 2-3 = -1 なので、順番は変わらない。
・3,4 を比較
=> 3-4 = -1 なので、また順番は変わらない。
・4,1 を比較
=> 4-1 = 3 なので、4 と 1 の順番を入れ替える。
=> [2, 3, 1, 4]
...

というようにソートが終わるまで繰り返すということなのかな?

つまり、二つの要素を比較して入れ替えるので降順にしたいなら、b-a とすればよいと。

最後に

JavaScript の sort() メソッドについて少しだけ理解が進んだような気がします。

参考

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/sort
https://teratail.com/questions/80029
https://cpoint-lab.co.jp/article/201902/8219/

Discussion