T-CREATOR

Lodash の sortBy で並び替えを自在にコントロール

Lodash の sortBy で並び替えを自在にコントロール

配列の並び替えは、フロントエンド開発において頻繁に遭遇する処理の一つです。ユーザーの一覧を名前順に表示したり、商品リストを価格順に並べたりと、データを見やすく整理することは良いユーザビリティの基盤となります。

JavaScript 標準の Array.sort() では複雑な並び替えが煩雑になりがちですが、Lodash の sortBy を使えば、驚くほどシンプルで直感的なコードで高度な並び替えが実現できるのです。

今回は、Lodash sortBy の基本的な使い方から応用的なテクニックまで、実際のコード例を交えながら詳しく解説していきます。この記事を読み終える頃には、あらゆる並び替えの要求に柔軟に対応できるようになるでしょう。

背景

JavaScript 標準の Array.sort() の制限

JavaScript の Array.sort() は基本的な並び替えには便利ですが、複雑な条件を扱うときには課題があります。

javascript// 基本的な数値並び替え
const numbers = [3, 1, 4, 1, 5];
numbers.sort((a, b) => a - b); // [1, 1, 3, 4, 5]

しかし、オブジェクトの配列を複数の条件で並び替えようとすると、コードが複雑になってしまいます。

javascript// 複雑になりがちな複数条件並び替え
const users = [
  { name: '田中', age: 25, score: 85 },
  { name: '佐藤', age: 30, score: 90 },
  { name: '鈴木', age: 25, score: 95 },
];

// 年齢順、同年齢なら点数の降順で並び替え
users.sort((a, b) => {
  if (a.age !== b.age) {
    return a.age - b.age;
  }
  return b.score - a.score;
});

複雑な並び替えロジックの実装の困難さ

実際の開発では、さらに複雑な並び替えが求められることがあります。

以下の図は、従来の JavaScript での並び替え処理の複雑さを示しています。

mermaidflowchart TD
  start[配列データ] --> check1{第1条件の比較}
  check1 -->|等しい| check2{第2条件の比較}
  check1 -->|異なる| result1[第1条件で並び替え]
  check2 -->|等しい| check3{第3条件の比較}
  check2 -->|異なる| result2[第2条件で並び替え]
  check3 --> result3[第3条件で並び替え]
  result1 --> final[並び替え完了]
  result2 --> final
  result3 --> final

このように、条件が増えるほど分岐処理が複雑になり、コードの可読性と保守性が損なわれてしまいます。

Lodash sortBy が解決する問題

Lodash の sortBy は、これらの課題を解決する強力なツールです。直感的な書き方で複雑な並び替えを実現し、コードの可読性を大幅に向上させます。

javascriptimport { sortBy } from 'lodash';

// 複数条件での並び替えがシンプルに
const sortedUsers = sortBy(users, ['age', 'name']);

図で理解できる要点:

  • 複数条件の並び替えが段階的に処理される
  • コードの複雑さが大幅に軽減される
  • 保守性と可読性が向上する

課題

複数条件での並び替え

実際の開発では、単一の条件だけでなく、複数の条件を組み合わせた並び替えが必要になることがよくあります。

例えば、以下のような要求があったとします:

  • 従業員リストを部署別に並び替え
  • 同じ部署内では役職順に並び替え
  • 同じ役職なら入社年数順に並び替え

ネストしたオブジェクトプロパティでの並び替え

深い階層を持つオブジェクトのプロパティで並び替えをする場合、アクセスパスが複雑になります。

javascriptconst products = [
  {
    name: 'ノートPC',
    specs: { price: { value: 120000, currency: 'JPY' } },
    category: { main: 'コンピューター', sub: 'ノートPC' },
  },
  // その他の商品データ...
];

このような構造で specs.price.valuecategory.main で並び替えをしたい場合、従来の方法では煩雑になってしまいます。

パフォーマンスとコードの可読性の両立

並び替え処理は、特に大量のデータを扱う場合にパフォーマンスが重要になります。しかし、パフォーマンスを追求するあまりコードが複雑になり、保守性が損なわれることがあります。

理想的には、以下の条件を満たす実装が求められます:

  • 高いパフォーマンス
  • 読みやすく理解しやすいコード
  • 変更に強い柔軟な設計

解決策

sortBy の基本構文と使い方

Lodash の sortBy は、非常にシンプルな構文で強力な並び替え機能を提供します。

javascriptimport { sortBy } from 'lodash';

// 基本的な使い方
const result = sortBy(配列, 並び替えの基準);

まずは、最もシンプルな例から見ていきましょう。

javascript// 数値配列の並び替え
const numbers = [3, 1, 4, 1, 5, 9, 2, 6];
const sortedNumbers = sortBy(numbers);
console.log(sortedNumbers); // [1, 1, 2, 3, 4, 5, 6, 9]
javascript// 文字列配列の並び替え
const fruits = ['バナナ', 'りんご', 'オレンジ', 'いちご'];
const sortedFruits = sortBy(fruits);
console.log(sortedFruits); // ['いちご', 'オレンジ', 'バナナ', 'りんご']

オブジェクトの配列を並び替える場合は、プロパティ名を指定します。

javascript// オブジェクト配列の並び替え
const users = [
  { name: '田中', age: 25 },
  { name: '佐藤', age: 30 },
  { name: '鈴木', age: 20 },
];

// 年齢順で並び替え
const sortedByAge = sortBy(users, 'age');
console.log(sortedByAge);
// [{ name: '鈴木', age: 20 }, { name: '田中', age: 25 }, { name: '佐藤', age: 30 }]

関数を使った動的並び替え

sortBy では、プロパティ名の代わりに関数を使用することで、より柔軟な並び替えが可能です。

javascript// 関数を使った並び替え
const products = [
  { name: 'ノートPC', price: 120000 },
  { name: 'スマートフォン', price: 80000 },
  { name: 'タブレット', price: 50000 },
];

// 価格の降順で並び替え(関数を使用)
const sortedByPriceDesc = sortBy(
  products,
  (product) => -product.price
);
console.log(sortedByPriceDesc);
// 価格の高い順に並び替えられる
javascript// 文字列の長さで並び替え
const words = ['JavaScript', 'HTML', 'CSS', 'TypeScript'];
const sortedByLength = sortBy(words, (word) => word.length);
console.log(sortedByLength); // ['CSS', 'HTML', 'JavaScript', 'TypeScript']

複数キーでの並び替え手法

sortBy の真の力は、複数の条件を組み合わせた並び替えにあります。

javascript// 複数条件での並び替え
const employees = [
  { name: '田中', department: '営業', salary: 400000 },
  { name: '佐藤', department: '開発', salary: 500000 },
  { name: '鈴木', department: '営業', salary: 450000 },
  { name: '高橋', department: '開発', salary: 480000 },
];

// 部署別、同部署内では給与順で並び替え
const sortedEmployees = sortBy(employees, [
  'department',
  'salary',
]);
console.log(sortedEmployees);

配列で複数の条件を指定することで、優先順位に従って並び替えが実行されます。最初の条件で同じ値の場合は、次の条件で比較されます。

javascript// 関数と文字列を組み合わせた複数条件
const sortedComplex = sortBy(employees, [
  'department', // 部署名(昇順)
  (employee) => -employee.salary, // 給与(降順)
]);

以下の図は、複数条件での並び替えの処理フローを示しています。

mermaidflowchart LR
  data[元データ] --> sort1[第1条件で並び替え]
  sort1 --> sort2[第2条件で並び替え]
  sort2 --> sort3[第3条件で並び替え]
  sort3 --> result[最終結果]

  subgraph "安定ソート"
    sort1
    sort2
    sort3
  end

sortBy は安定ソートを使用するため、前の条件での順序が保持されながら次の条件が適用されます。

具体例

文字列・数値での基本並び替え

実際のプロジェクトでよく使われる基本的な並び替えパターンを見ていきましょう。

javascriptimport { sortBy } from 'lodash';

// 文字列配列の並び替え(日本語対応)
const prefectures = [
  '東京都',
  '大阪府',
  '北海道',
  '沖縄県',
  '愛知県',
];
const sortedPrefectures = sortBy(prefectures);
console.log(sortedPrefectures);
// ['愛知県', '大阪府', '沖縄県', '北海道', '東京都']
javascript// 数値配列の並び替え
const scores = [85, 92, 78, 95, 88, 76];
const sortedScores = sortBy(scores);
console.log(sortedScores); // [76, 78, 85, 88, 92, 95]

// 降順で並び替えたい場合
const sortedScoresDesc = sortBy(scores, (score) => -score);
console.log(sortedScoresDesc); // [95, 92, 88, 85, 78, 76]

オブジェクト配列の並び替え

EC サイトの商品一覧のような、実際のビジネスロジックでよく見る例を実装してみましょう。

javascript// 商品データの例
const products = [
  {
    id: 1,
    name: 'ワイヤレスヘッドホン',
    price: 15800,
    category: 'オーディオ',
    rating: 4.5,
    inStock: true,
  },
  {
    id: 2,
    name: 'Bluetooth スピーカー',
    price: 8900,
    category: 'オーディオ',
    rating: 4.2,
    inStock: false,
  },
  {
    id: 3,
    name: 'スマートウォッチ',
    price: 25000,
    category: 'ウェアラブル',
    rating: 4.7,
    inStock: true,
  },
];
javascript// 価格の安い順で並び替え
const sortedByPrice = sortBy(products, 'price');
console.log(
  '価格順:',
  sortedByPrice.map((p) => `${p.name}: ¥${p.price}`)
);
javascript// 評価の高い順で並び替え
const sortedByRating = sortBy(
  products,
  (product) => -product.rating
);
console.log(
  '評価順:',
  sortedByRating.map((p) => `${p.name}: ${p.rating}★`)
);
javascript// 在庫あり商品を優先し、その中で価格の安い順
const sortedByStockAndPrice = sortBy(products, [
  (product) => !product.inStock, // 在庫ありを優先(false が先に来る)
  'price', // 価格の安い順
]);
console.log('在庫・価格順:', sortedByStockAndPrice);

複雑な条件での並び替え実装

より実践的な例として、従業員管理システムでの複雑な並び替えを実装してみます。

javascript// 従業員データの例
const employees = [
  {
    id: 1,
    name: '田中太郎',
    position: 'マネージャー',
    department: '営業部',
    hireDate: '2020-04-01',
    salary: 550000,
    performance: 'A',
  },
  {
    id: 2,
    name: '佐藤花子',
    position: 'エンジニア',
    department: '開発部',
    hireDate: '2019-07-15',
    salary: 480000,
    performance: 'B',
  },
  {
    id: 3,
    name: '鈴木一郎',
    position: 'エンジニア',
    department: '開発部',
    hireDate: '2021-01-10',
    salary: 420000,
    performance: 'A',
  },
];
javascript// 部署別、同部署内では役職別、同役職では評価順で並び替え
const departmentOrder = { 開発部: 1, 営業部: 2, 総務部: 3 };
const positionOrder = {
  マネージャー: 1,
  リーダー: 2,
  エンジニア: 3,
};
const performanceOrder = { A: 1, B: 2, C: 3 };

const complexSorted = sortBy(employees, [
  (emp) => departmentOrder[emp.department] || 999,
  (emp) => positionOrder[emp.position] || 999,
  (emp) => performanceOrder[emp.performance] || 999,
]);

console.log('複合条件並び替え:', complexSorted);
javascript// 入社年数を計算して並び替え
const currentDate = new Date();
const sortedByExperience = sortBy(employees, (employee) => {
  const hireDate = new Date(employee.hireDate);
  const yearsDiff =
    currentDate.getFullYear() - hireDate.getFullYear();
  return -yearsDiff; // 経験年数の長い順(降順)
});

console.log('経験年数順:', sortedByExperience);

ネストしたプロパティでの並び替え

深い階層を持つオブジェクトの並び替えも、sortBy なら簡潔に記述できます。

javascript// ネストした構造のデータ例
const companies = [
  {
    name: 'テックカンパニーA',
    location: {
      country: '日本',
      prefecture: '東京都',
      city: '渋谷区',
    },
    financial: {
      revenue: 50000000,
      employees: 120,
    },
  },
  {
    name: 'スタートアップB',
    location: {
      country: '日本',
      prefecture: '大阪府',
      city: '大阪市',
    },
    financial: {
      revenue: 12000000,
      employees: 45,
    },
  },
];
javascript// ネストしたプロパティでの並び替え
const sortedByRevenue = sortBy(
  companies,
  'financial.revenue'
);
console.log('売上順:', sortedByRevenue);

// より複雑なネストプロパティの場合は関数を使用
const sortedByLocation = sortBy(companies, [
  'location.prefecture',
  (company) => company.financial.employees,
]);
console.log('地域・従業員数順:', sortedByLocation);

以下の図は、sortBy による多段階並び替えの概念を示しています。

mermaidsequenceDiagram
    participant Client as クライアント
    participant SortBy as sortBy関数
    participant Data as データ配列

    Client->>SortBy: sortBy(data, ['dept', 'salary'])
    SortBy->>Data: 第1条件:部署名で並び替え
    Data-->>SortBy: 部署別グループ化完了
    SortBy->>Data: 第2条件:各部署内で給与順並び替え
    Data-->>SortBy: 最終並び替え完了
    SortBy-->>Client: 並び替え済み配列を返却

図で理解できる要点:

  • 複数条件は順番に処理される
  • 前の条件での順序は保持される
  • ネストしたプロパティも直感的にアクセス可能

まとめ

sortBy の活用メリット

Lodash の sortBy を使用することで、以下のような大きなメリットが得られます。

コードの簡潔性と可読性の向上

従来の Array.sort() では複雑になりがちな多条件並び替えが、非常にシンプルな記述で実現できます。特に複数の条件を配列で指定できる機能は、コードの可読性を大幅に向上させます。

メンテナンス性の向上

並び替え条件の追加や変更が容易になり、チーム開発での保守性が向上します。新しい開発者でも直感的に理解できるコードが書けるため、引き継ぎやレビューの効率も上がります。

パフォーマンスの最適化

Lodash は内部で最適化されたアルゴリズムを使用しており、手動実装よりも高いパフォーマンスが期待できます。特に大量のデータを扱う場合、その差は顕著に現れます。

実際の開発での効果的な使い方

実践的な開発において sortBy を効果的に活用するためのポイントをまとめます。

TypeScript との組み合わせ

typescriptinterface Product {
  name: string;
  price: number;
  category: string;
}

const products: Product[] = [...];
const sorted = sortBy(products, ['category', 'price']);

型安全性を保ちながら、sortBy の恩恵を受けることができます。

パフォーマンスを考慮した実装

大量データを扱う場合は、並び替え条件を事前に計算しておくことで、パフォーマンスを向上させられます。

javascript// 重い計算を事前に実行
const productsWithScore = products.map((product) => ({
  ...product,
  score: calculateComplexScore(product), // 重い計算
}));

// 事前計算した値で並び替え
const sorted = sortBy(productsWithScore, 'score');

チーム開発での活用

sortBy を使用することで、チーム内でのコードの統一性が向上し、レビューや保守が効率化されます。特に、並び替えロジックが複雑になりがちなプロジェクトでは、その効果は絶大です。

Lodash の sortBy は、JavaScript での配列並び替えをより簡単で強力なものにしてくれる優秀なツールです。基本的な使い方から応用的なテクニックまでを習得することで、様々な並び替え要求に柔軟に対応できるようになります。

ぜひ実際のプロジェクトで活用して、よりクリーンで保守性の高いコードを書いてみてください。

関連リンク