JavaScriptで配列のソートを行うには、配列.sort()メソッドを使うのが手っ取り早い。ただ条件(正確には比較関数)の書き方が簡単そうで難しい。

配列.sort()の基本的な使い方

JavaScriptの配列のsort()メソッドは、UTF-16のコードポイントの値に基づいて昇順でソートを行う。これは、元の配列をコピーせずに並び替える破壊的操作である。

並び替えはその場で行われるため、メモリはほとんど消費されない。また、返される値は並び替えが終わった同じ配列の参照である。

javascript
const sample = ["C", "F", "G", "C"];
console.log(sample.sort());
// ["C", "C", "F", "G"]
console.log(sample);
// ["C", "C", "F", "G"]

ちなみに元の配列を破壊せずにソートしたい時はtoSorted()メソッドを使う。

javascript
const sample = ["C", "F", "G", "C"];
console.log(sample.toSorted());
// ["C", "C", "F", "G"]
console.log(sample);
// ["C", "F", "G", "C"]

注意したいのは、いずれの場合も2桁以上の整数を正しくソートできない点である。UTF-16では、数値もアルファベットも昇順に文字コードが割り振られている。

ただ、両者のデフォルトの比較関数は、1文字単位で並び替えを行うため、2よりも10の方が小さいと判断されてしまう。

javascript
console.log([2, 10, 0].sort());
// [0, 10, 2]
console.log([2, 10, 0].toSorted());
// [0, 10, 2]

整数を比較したいときは、並び替え方法を別途指定する必要がある。

並び替え方法の指定

sort()メソッドのソート方法をコントロールするには、引数に比較用の関数を渡せばよい。

この関数は、引数に2つの値を受け取る。これは配列の前後の要素である。関数内でこれら2つの値をあれこれして、正負、あるいは0のいずれかで値を返すと、sort()メソッドがいい感じに並び替えをしてくれる。

例えばabを引数に受け取ったとすると、返す値によってソートのパターンは以下のように決定される。

  • 正のとき:abの後ろに並び替える。
  • 負のとき:abの前に並び替える。
  • 0のとき:abの元々の順番を維持する。

混乱するポイント

javascript
const sample = [0, 2, 1];
console.log(sample.sort((a, b) => a - b));
// [0, 1, 2]
console.log(sample.sort((a, b) => b - a));
// [2, 1, 0]

a - bの場合、その結果が負のときは「aの方が小さい」、と判断する。正のときは「aの方が大きい」、と判断する。どちらのケースも、現在の並び順に応じて、昇順に並び替えを行う。

b - aの場合、その結果が負のときは「bの方が小さい」、と判断する。正のときは「bの方が大きい」、と判断する。どちらのケースも、現在の並び順に応じて、昇順に並び替えを行う。

a - bと、b - aで、何がちがうの? と混乱するかもしれない。書いていて自分でも混乱した。何がちがうかといえば、符合が違う。そのため大小が逆転し、アルゴリズムが同じでもソートの昇順と降順が入れ替わる。

文字列を任意の順番にソートする

JavaScriptの配列のsort()メソッドを使ったソートは、比較関数が返す正負あるいは0の値によって決定される。

そのため、文字列の配列を任意の順番に並び替えたいときは、数値ベースで処理できるよう事前に準備してやるのが早道である。

例えば月名を並び替える処理は、以下のようになる。

javascript
const monthOrder = {
  January: 1,
  February: 2,
  March: 3,
  April: 4,
  May: 5,
  June: 6,
  July: 7,
  August: 8,
  September: 9,
  October: 10,
  November: 11,
  December: 12,
};

const months = ["March", "January", "February", "December", "November"];
months.sort((a, b) => monthOrder[a] - monthOrder[b]);
console.log(months);
// ["January", "February", "March", "November", "December"]

おしまい。

参考資料