【JavaScript】ソート機能を実装する方法

JavaScriptのソートは Array.prototype.sort() で行いますが、使い方を間違えると数値が正しく並ばないという有名な落とし穴があります。この記事では、その落とし穴を押さえながら、配列・オブジェクト・HTMLテーブルのソートを実装します。

この記事の結論:ソートは必ず比較関数を渡します。数値は (a, b) => a - b、文字列は (a, b) => a.localeCompare(b)引数なしの sort() は文字列として比較するため、[1, 10, 2].sort() は数値順になりません。また sort()元の配列を変更するので、残したいときは toSorted() を使います。
スポンサーリンク

配列のソートと「sortの落とし穴」

まず基本の数値ソートです。比較関数 (a, b) => a - b を必ず渡します。結果が負ならaが前、正ならbが前に並びます。

JavaScript:数値のソート
const numbers = [3, 1, 10, 2];
numbers.sort((a, b) => a - b);
console.log(numbers); // [1, 2, 3, 10]
最大の落とし穴:比較関数を省くと、sort() は要素を文字列に変換して比較します。そのため数値でも辞書順になり、意図しない並びになります。
[1, 10, 2].sort()[1, 10, 2](”1″ < “10” < “2” の順)。数値を並べるときは必ず (a, b) => a - b を渡してください。

文字列は localeCompare() を使います。これも引数なしの sort() だと大文字小文字や日本語で崩れます

JavaScript:文字列のソート
const fruits = ["banana", "apple", "cherry"];
fruits.sort((a, b) => a.localeCompare(b));
console.log(fruits); // ["apple", "banana", "cherry"]

// 落とし穴: 引数なしだと大文字が先に来る(UTF-16コード順)
console.log(["Banana", "apple", "Cherry"].sort());
// ["Banana", "Cherry", "apple"]  ← 大文字 B,C が小文字 a より前
// localeCompare なら ["apple", "Banana", "Cherry"] と自然な順になる

配列操作全般(mapfilter など)は配列のmap・filter・reduceの使い方、条件で要素を絞る方法は配列から条件に合う要素を取り出す方法を参考にしてください。

オブジェクト配列のソート

オブジェクトの配列は、ソート基準となるプロパティを比較関数で指定します。

JavaScript:プロパティでソート
const people = [
  { name: "John", age: 25 },
  { name: "Jane", age: 30 },
  { name: "Tom", age: 20 },
];

// 数値プロパティ(年齢の昇順)
people.sort((a, b) => a.age - b.age);

// 文字列プロパティ(名前順)
people.sort((a, b) => a.name.localeCompare(b.name));

複数の基準で並べたいときは、|| で比較をつなげます。前の比較が 0(同値)のときだけ次の基準で比較されます。

JavaScript:複数キーでソート
// 名前順 → 同じ名前なら年齢の昇順
people.sort((a, b) => a.name.localeCompare(b.name) || a.age - b.age);

昇順・降順と「非破壊」ソート

降順にするには、比較関数の ab を入れ替えます。また sort()元の配列を書き換える(破壊的)点に注意します。

JavaScript:降順と非破壊ソート
const nums = [3, 1, 2];

// 降順: a と b を逆にする
console.log([...nums].sort((a, b) => b - a)); // [3, 2, 1]

// sort() は元の配列を変更してしまう
nums.sort((a, b) => a - b);
console.log(nums); // [1, 2, 3] ← 元が変わっている

// 元を残したいなら toSorted()(ES2023)か、コピーしてからソート
const sorted = nums.toSorted((a, b) => b - a);
// 古い環境では: const sorted = [...nums].sort((a, b) => b - a);

HTMLテーブルのソート(昇順↔降順トグル)

テーブルは、ヘッダーのクリックで行を並べ替えます。同じ列を再クリックしたら昇順・降順を反転させると使いやすくなります。セル値は innerText ではなくtextContentで取得します(余計なリフローを避けられます)。

HTML
<table id="myTable">
  <thead>
    <tr><th>名前</th><th>年齢</th></tr>
  </thead>
  <tbody>
    <tr><td>John</td><td>25</td></tr>
    <tr><td>Jane</td><td>30</td></tr>
    <tr><td>Tom</td><td>20</td></tr>
  </tbody>
</table>
JavaScript:トグル付きテーブルソート
const table = document.getElementById("myTable");
let sortState = { col: -1, asc: true };

// ヘッダーにクリックイベントを設定
table.querySelectorAll("thead th").forEach((th, index) => {
  th.addEventListener("click", () => sortTable(index));
});

function sortTable(colIndex) {
  const tbody = table.tBodies[0];
  const rows = Array.from(tbody.rows);

  // 同じ列の再クリックで方向を反転
  sortState.asc = sortState.col === colIndex ? !sortState.asc : true;
  sortState.col = colIndex;
  const dir = sortState.asc ? 1 : -1;

  rows.sort((rowA, rowB) => {
    const a = rowA.cells[colIndex].textContent.trim();
    const b = rowB.cells[colIndex].textContent.trim();

    // 両方が数値なら数値比較、それ以外は文字列比較
    const numA = Number(a), numB = Number(b);
    const bothNumbers = a !== "" && b !== "" && !isNaN(numA) && !isNaN(numB);
    const result = bothNumbers ? numA - numB : a.localeCompare(b);
    return result * dir;
  });

  rows.forEach((row) => tbody.appendChild(row)); // 並び替えた順で再配置
}

どの列をどの方向でソート中かを示す矢印(▲▼)は、ヘッダーにクラスを付け外しして表現できます。クラス操作はclassListの使い方が参考になります。

よくある質問(FAQ)

QJavaScriptでソート機能を実装するには?
A配列の sort() に比較関数を渡します。数値昇順なら (a, b) => a - b、文字列なら (a, b) => a.localeCompare(b) です。DOMテーブルは、行を配列化してソートし、並び替えた順で再配置します。
Q数値を sort() したら順番がおかしくなります。なぜ?
A比較関数を省くと、sort() は要素を文字列に変換して辞書順で並べるためです。[1, 10, 2].sort()[1, 10, 2] になります。数値は必ず (a, b) => a - b を渡してください。
Qクリックで昇順・降順を切り替えるソートはどう実装しますか?
Aソート方向(昇順/降順)をフラグで管理し、同じ列を再クリックするたびに反転させます。比較結果に 1(昇順)か -1(降順)を掛けることで方向を制御できます。
Q複数列でのソートはどう実装しますか?
A比較を || でつなぎます。前の比較が 0(同値)のとき次のキーで比較されます。例:(a, b) => a.name.localeCompare(b.name) || a.age - b.age

まとめ

JavaScriptのソートは、必ず比較関数を渡すのが鉄則です。数値は (a, b) => a - b、文字列は localeCompare引数なしの sort() は文字列順になり、[1, 10, 2] のような数値が正しく並ばない点に注意してください。

降順は ab を入れ替え、複数キーは || で連結します。元の配列を残したいときは toSorted() を使いましょう。テーブルソートは、再クリックで昇順↔降順を反転させると使いやすくなります。