更新: 2026年03月 · 10 分で読める · 4,937 文字
JavaScriptの配列ソートを使い分ける:sort()の落とし穴と実装パターン
JavaScriptのsort()メソッドは配列を並び替える基本機能ですが、デフォルトの動作が予想外の結果を生むことがあります。この記事では、数値ソート、オブジェクト配列のソート、カスタム比較ロジックなど、実務で即座に活用できる4つのソートパターンと、よくあるエラーの回避方法を紹介します。
JavaScriptの配列ソートが複雑な理由
JavaScriptのsort()メソッドはデフォルトで配列要素を文字列として比較します。そのため、[10, 5, 40, 25].sort()は[10, 25, 40, 5]という直感に反した結果になります。これが多くの開発者を困らせる主な原因です。
パターン1:数値配列の昇順・降順ソート
昇順ソート(小から大へ)
数値を正しく並び替えるには、比較関数を指定する必要があります。
const numbers = [10, 5, 40, 25, 1000];
// 正しい昇順ソート
const sorted = numbers.sort((a, b) => a - b);
console.log(sorted); // [5, 10, 25, 40, 1000]
// よくある間違い(文字列比較になる)
const wrong = numbers.sort();
console.log(wrong); // [1, 10, 25, 40, 5] ← 期待値と異なる!
降順ソート(大から小へ)
const numbers = [10, 5, 40, 25, 1000];
// 降順ソート
const descending = numbers.sort((a, b) => b - a);
console.log(descending); // [1000, 40, 25, 10, 5]
元の配列を変更しない方法
sort()は元の配列を変更します(破壊的操作)。元の配列を保持したい場合は、スプレッド演算子でコピーを作成してからソートします。
const numbers = [10, 5, 40, 25];
const original = numbers;
// 方法1:スプレッド演算子でコピーしてソート
const sorted = [...numbers].sort((a, b) => a - b);
console.log(original); // [10, 5, 40, 25] ← 変わらない
console.log(sorted); // [5, 10, 25, 40]
// 方法2:slice()でコピーしてソート
const sorted2 = numbers.slice().sort((a, b) => a - b);
パターン2:オブジェクト配列のソート
実務ではユーザーデータや商品情報など、オブジェクト配列をソートする場面が多くあります。
特定プロパティで昇順ソート
const users = [
{ id: 1, name: '田中', age: 28 },
{ id: 2, name: '鈴木', age: 35 },
{ id: 3, name: '佐藤', age: 22 }
];
// ageプロパティで昇順ソート
const sortedByAge = [...users].sort((a, b) => a.age - b.age);
console.log(sortedByAge);
// [{ id: 3, name: '佐藤', age: 22 }, ...]
// nameプロパティで昇順ソート(文字列なのでlocaleCompareを使用)
const sortedByName = [...users].sort((a, b) =>
a.name.localeCompare(b.name)
);
console.log(sortedByName);
// [{ id: 1, name: '田中', ... }, { id: 3, name: '佐藤', ... }, ...]
複数条件でソート
部門ごとに分類し、各部門内で給与が高い順に並び替えるような場合です。
const employees = [
{ name: '太郎', department: '営業', salary: 400000 },
{ name: '花子', department: 'IT', salary: 500000 },
{ name: '次郎', department: '営業', salary: 450000 },
{ name: '美咲', department: 'IT', salary: 480000 }
];
// まず部門でソート、次に同じ部門内で給与で降順ソート
const sorted = [...employees].sort((a, b) => {
// 第1条件:department(昇順)
if (a.department !== b.department) {
return a.department.localeCompare(b.department);
}
// 第2条件:salary(降順)
return b.salary - a.salary;
});
console.log(sorted);
// [{ name: '花子', department: 'IT', salary: 500000 }, ...]
パターン3:日付のソート
Date オブジェクトの比較
const events = [
{ title: '会議A', date: new Date('2025-03-15') },
{ title: '会議B', date: new Date('2025-03-10') },
{ title: '会議C', date: new Date('2025-03-20') }
];
// 日付の早い順(昇順)でソート
const sorted = [...events].sort((a, b) => a.date - b.date);
console.log(sorted);
// 2025-03-10 → 2025-03-15 → 2025-03-20 の順
// 日付の新しい順(降順)でソート
const reversed = [...events].sort((a, b) => b.date - a.date);
ISO文字列日付でソート
const logs = [
{ action: 'ログイン', timestamp: '2025-03-15T10:30:00Z' },
{ action: 'ログアウト', timestamp: '2025-03-15T09:15:00Z' },
{ action: 'ファイル作成', timestamp: '2025-03-15T10:45:00Z' }
];
// ISO文字列は辞書順比較で正しくソートできる
const sorted = [...logs].sort((a, b) =>
a.timestamp.localeCompare(b.timestamp)
);
パターン4:カスタムソート関数の実装
複雑なロジックの再利用可能な関数化
// 汎用的なオブジェクト配列ソート関数
function sortByProperty(array, property, order = 'asc') {
const sorted = [...array];
return sorted.sort((a, b) => {
const valueA = a[property];
const valueB = b[property];
// nullやundefinedの処理
if (valueA == null) return 1;
if (valueB == null) return -1;
// 文字列の場合
if (typeof valueA === 'string') {
const comparison = valueA.localeCompare(valueB);
return order === 'asc' ? comparison : -comparison;
}
// 数値の場合
if (typeof valueA === 'number') {
return order === 'asc' ? valueA - valueB : valueB - valueA;
}
return 0;
});
}
// 使用例
const users = [
{ name: '山田', score: 85 },
{ name: null, score: 90 },
{ name: '鈴木', score: 78 }
];
const sorted = sortByProperty(users, 'score', 'desc');
console.log(sorted);
// [{ name: null, score: 90 }, { name: '山田', score: 85 }, ...]
ハマりやすいポイント&解決策
問題1:大文字と小文字が区別される
const words = ['apple', 'Banana', 'cherry', 'Date'];
// デフォルト比較(大文字が先になる)
console.log(words.sort());
// ['Banana', 'Date', 'apple', 'cherry']
// 大文字小文字を区別しないソート
const caseInsensitive = [...words].sort((a, b) =>
a.toLowerCase().localeCompare(b.toLowerCase())
);
console.log(caseInsensitive);
// ['apple', 'Banana', 'cherry', 'Date']
問題2:日本語の順序が正しくない
const cities = ['東京', '大阪', '京都', '青森'];
// 正しい日本語ソート(localeCompareを使用)
const sorted = [...cities].sort((a, b) => a.localeCompare(b, 'ja'));
console.log(sorted);
// ['青森', '大阪', '京都', '東京']
問題3:小数点がある数値のソート
const prices = [10.5, 10.05, 10.50, 10.005];
// 単純な減算だと浮動小数点誤差が発生することがある
const sorted1 = [...prices].sort((a, b) => a - b);
// より安全な比較方法
const sorted2 = [...prices].sort((a, b) => {
if (a < b) return -1;
if (a > b) return 1;
return 0;
});
console.log(sorted2); // [10.005, 10.05, 10.5, 10.50]
ソート使用時の判断基準
| 使うべき場面 | 使うべきでない場面 |
|---|---|
| UIリストの表示順序 | データベースクエリで大規模データ |
| クライアント側での小~中規模データ処理 | リアルタイム処理が必要な場合 |
| ページネーション前の結果整形 | ソート結果を頻繁に変更する場合 |
よくある質問
はい、sort()は破壊的メソッドで、元の配列を変更します。元の配列を保持したい場合は、spread演算子(...)やslice()でコピーしてからソートしてください。
JavaScriptエンジンは自動的に効率的なソートアルゴリズム(クイックソートやマージソート)を選択するため、通常はクライアント側でのソートは避け、バックエンド(SQLなど)でソートしたデータを取得することが推奨されます。どうしてもクライアント側で処理する場合は、部分的なデータのみを表示するページネーション機能の実装をご検討ください。
複数条件のソートは冗長になりやすいため、ライブラリの使用も検討できます。ただし、シンプルな2~3条件であれば&&演算子で短縮できます:const sorted = [...data].sort((a, b) => a.dept.localeCompare(b.dept) || b.salary - a.salary);
まとめ
- JavaScriptの
sort()はデフォルトで文字列比較。数値ソートには(a, b) => a - bの比較関数が必須 - 元の配列を保持したい場合は、
spread演算子またはslice()でコピーしてからソート - オブジェクト配列のソートには、プロパティの型に応じて減算(数値)または
localeCompare()(文字列)を使い分ける - 複数条件でのソートは、比較関数内で第1条件が同じ場合に第2条件を評価する論理を実装
- 大規模データ
おすすめフロントエンドリソース
- MDN Web Docs The most trusted reference for HTML, CSS, and JavaScript.
- React Documentation Official React tutorials and API reference.
- Can I Use Essential tool for checking browser compatibility.