T-CREATOR

Lodash の groupBy で集計・グルーピングを簡単実装

Lodash の groupBy で集計・グルーピングを簡単実装

JavaScript での配列データ処理において、特定の条件でデータをグルーピングしたい場面は数多く存在します。Lodash の groupBy 関数を使えば、複雑なグルーピング処理を簡潔で読みやすいコードで実現できるのです。

本記事では、Lodash の groupBy を使った効率的なデータグルーピングの方法を、基本的な使い方から応用例まで詳しく解説いたします。初心者の方でもすぐに実践できるよう、豊富なサンプルコードとともにご紹介します。

背景

JavaScript での配列データのグルーピングの必要性

現代の Web アプリケーション開発では、API から取得したデータや CSV ファイルのデータなど、大量の配列データを扱う機会が非常に多くなっています。

これらのデータを効果的に活用するためには、特定の条件でデータをグループ化し、集計や分析を行う処理が欠かせません。例えば、ユーザー管理システムでは年齢別のユーザー数を集計したり、EC サイトでは商品カテゴリ別の売上を分析したりする必要があるでしょう。

以下の図は、一般的なデータグルーピングのフローを示しています。

mermaidflowchart LR
    data[生データ配列] -->|条件指定| group[グルーピング処理]
    group -->|分類| result[グループ別データ]
    result -->|活用| analysis[集計・分析]
    result -->|表示| ui[UI表示]

このように、データグルーピングは様々な用途の基盤となる重要な処理なのです。

生の JavaScript でのグルーピング処理の複雑さ

生の JavaScript でデータグルーピングを実装しようとすると、かなり複雑なコードになってしまいます。

javascript// 生のJavaScriptでのグルーピング実装例
const users = [
  { name: '田中太郎', age: 25, department: '営業部' },
  { name: '佐藤花子', age: 30, department: '開発部' },
  { name: '鈴木一郎', age: 25, department: '営業部' },
];

// 部署別にグルーピングする処理
function groupByDepartment(users) {
  const result = {};

  for (let i = 0; i < users.length; i++) {
    const user = users[i];
    const key = user.department;

    if (!result[key]) {
      result[key] = [];
    }
    result[key].push(user);
  }

  return result;
}

このコードには以下のような問題があります:

javascript// さらに複雑な条件の場合
function groupByAgeRange(users) {
  const result = {};

  users.forEach((user) => {
    let ageRange;
    if (user.age < 30) {
      ageRange = '20代';
    } else if (user.age < 40) {
      ageRange = '30代';
    } else {
      ageRange = '40代以上';
    }

    if (!result[ageRange]) {
      result[ageRange] = [];
    }
    result[ageRange].push(user);
  });

  return result;
}

このように、条件が複雑になるほどコードの可読性が下がり、バグの温床となってしまうのです。

Lodash.groupBy の登場と利便性

Lodash は JavaScript の便利なユーティリティ関数を提供するライブラリです。その中でも groupBy 関数は、配列データのグルーピング処理を劇的に簡潔にしてくれます。

まずは Lodash のインストール方法から確認しましょう。

bash# npm を使用する場合
npm install lodash

# yarn を使用する場合
yarn add lodash

TypeScript プロジェクトの場合は、型定義ファイルもインストールします。

bash# 型定義ファイルのインストール
yarn add @types/lodash

基本的なインポート方法は以下のようになります。

javascript// 全体をインポートする場合
import _ from 'lodash';

// groupBy のみをインポートする場合(推奨)
import { groupBy } from 'lodash';

先ほどの複雑だった処理が、Lodash の groupBy を使うとこれほどシンプルになります。

javascriptimport { groupBy } from 'lodash';

const users = [
  { name: '田中太郎', age: 25, department: '営業部' },
  { name: '佐藤花子', age: 30, department: '開発部' },
  { name: '鈴木一郎', age: 25, department: '営業部' },
];

// 部署別グルーピング(1行で完了!)
const groupedByDepartment = groupBy(users, 'department');

この簡潔さと可読性の高さが、Lodash groupBy の最大の魅力なのです。

課題

複雑なデータ構造からの効率的なグルーピング

実際のプロジェクトでは、単純な配列だけでなく、ネストしたオブジェクトや複数の条件を組み合わせたグルーピングが必要になることがあります。

以下の図は、複雑なデータ構造でのグルーピング処理の課題を示しています。

mermaidflowchart TD
    A[複雑なデータ構造] --> B{グルーピング条件}
    B -->|単一条件| C[シンプルなグルーピング]
    B -->|複数条件| D[複合グルーピング]
    B -->|ネストした属性| E[深い階層のグルーピング]
    B -->|計算結果| F[動的グルーピング]

    C --> G[実装が比較的簡単]
    D --> H[条件の組み合わせが複雑]
    E --> I[オブジェクトの深い参照が必要]
    F --> J[実行時の計算処理が必要]

例えば、以下のような複雑なデータ構造を考えてみましょう。

javascriptconst complexData = [
  {
    user: {
      profile: { age: 25, location: '東京' },
      settings: { theme: 'dark' },
    },
    activity: { lastLogin: '2024-01-15', score: 85 },
  },
  {
    user: {
      profile: { age: 30, location: '大阪' },
      settings: { theme: 'light' },
    },
    activity: { lastLogin: '2024-01-10', score: 92 },
  },
];

このような構造からネストしたプロパティでグルーピングする場合、従来の方法では非常に煩雑な処理になってしまいます。

パフォーマンスを考慮したデータ処理

大量のデータを扱う際には、処理速度とメモリ使用量が重要な課題となります。

以下の表は、データ量によるパフォーマンス課題を整理したものです。

#データ量生 JavaScriptLodash groupBy課題
11,000 件高速高速特になし
210,000 件普通高速実装の複雑さ
3100,000 件低速高速メモリ使用量
41,000,000 件非常に低速普通処理時間

特に、React や Vue.js などの SPA では、ユーザーインターフェースの応答性を保つために、効率的なデータ処理が求められます。

javascript// パフォーマンスが問題となる例
const handleLargeDataGrouping = (largeDataSet) => {
  // 毎回の状態更新でグルーピング処理が実行される
  const grouped = groupBy(largeDataSet, 'category');
  setGroupedData(grouped);
};

このような場面では、適切なメモ化やキャッシュ機能との組み合わせが重要になってきます。

可読性の高いコード実装

チーム開発において、コードの可読性と保守性は非常に重要な要素です。

複雑なグルーピング条件を実装する際、以下のような課題が発生しがちです:

javascript// 可読性が低い実装例
const groupedData = data.reduce((acc, item) => {
  const key =
    item.type === 'A'
      ? 'typeA'
      : item.type === 'B' && item.priority > 5
      ? 'highPriorityB'
      : item.type === 'B'
      ? 'lowPriorityB'
      : 'other';

  if (!acc[key]) acc[key] = [];
  acc[key].push(item);
  return acc;
}, {});

このようなコードは、後から読む人(未来の自分も含めて)にとって理解が困難になってしまいますね。

Lodash groupBy を適切に活用することで、これらの課題を解決し、保守性の高いコードを実現できるのです。

解決策

Lodash.groupBy の基本的な使い方

Lodash の groupBy 関数の基本構文は非常にシンプルです。

javascriptimport { groupBy } from 'lodash';

// 基本構文
const result = groupBy(collection, iteratee);

第一引数には配列を、第二引数にはグルーピングのキーとなる値を指定します。第二引数は文字列、関数、オブジェクトなど、様々な形式で指定できるのが特徴です。

最もシンプルな使い方から見ていきましょう。

javascriptconst fruits = [
  'apple',
  'banana',
  'cherry',
  'apricot',
  'blueberry',
];

// 文字列の最初の文字でグルーピング
const groupedByFirstLetter = groupBy(
  fruits,
  (fruit) => fruit[0]
);

console.log(groupedByFirstLetter);
// {
//   a: ['apple', 'apricot'],
//   b: ['banana', 'blueberry'],
//   c: ['cherry']
// }

オブジェクト配列の場合は、プロパティ名を文字列で指定するだけでグルーピングできます。

javascriptconst products = [
  { name: 'iPhone', category: 'smartphone', price: 100000 },
  { name: 'MacBook', category: 'laptop', price: 200000 },
  { name: 'iPad', category: 'tablet', price: 80000 },
  { name: 'Android', category: 'smartphone', price: 70000 },
];

// カテゴリ別にグルーピング
const groupedByCategory = groupBy(products, 'category');

console.log(groupedByCategory);
// {
//   smartphone: [
//     { name: 'iPhone', category: 'smartphone', price: 100000 },
//     { name: 'Android', category: 'smartphone', price: 70000 }
//   ],
//   laptop: [
//     { name: 'MacBook', category: 'laptop', price: 200000 }
//   ],
//   tablet: [
//     { name: 'iPad', category: 'tablet', price: 80000 }
//   ]
// }

様々なキーでのグルーピング手法

groupBy の真価は、様々な条件でのグルーピングができることです。

関数を使った動的なグルーピングを見てみましょう。

javascriptconst orders = [
  { id: 1, amount: 1500, date: '2024-01-15' },
  { id: 2, amount: 3200, date: '2024-01-16' },
  { id: 3, amount: 800, date: '2024-01-15' },
  { id: 4, amount: 5500, date: '2024-01-17' },
];

// 金額レンジでグルーピング
const groupedByAmountRange = groupBy(orders, (order) => {
  if (order.amount < 1000) return '小額';
  if (order.amount < 3000) return '中額';
  return '高額';
});

console.log(groupedByAmountRange);
// {
//   中額: [
//     { id: 1, amount: 1500, date: '2024-01-15' }
//   ],
//   高額: [
//     { id: 2, amount: 3200, date: '2024-01-16' },
//     { id: 4, amount: 5500, date: '2024-01-17' }
//   ],
//   小額: [
//     { id: 3, amount: 800, date: '2024-01-15' }
//   ]
// }

複雑な条件を組み合わせたグルーピングも可能です。

javascriptconst employees = [
  {
    name: '田中',
    age: 28,
    department: '営業',
    experience: 3,
  },
  {
    name: '佐藤',
    age: 35,
    department: '開発',
    experience: 8,
  },
  {
    name: '鈴木',
    age: 42,
    department: '営業',
    experience: 15,
  },
  {
    name: '高橋',
    age: 29,
    department: '開発',
    experience: 5,
  },
];

// 部署と経験年数の組み合わせでグルーピング
const groupedByDeptAndExp = groupBy(employees, (emp) => {
  const expLevel =
    emp.experience < 5
      ? '新人'
      : emp.experience < 10
      ? 'シニア'
      : 'ベテラン';
  return `${emp.department}_${expLevel}`;
});

console.log(groupedByDeptAndExp);
// {
//   営業_新人: [{ name: '田中', age: 28, department: '営業', experience: 3 }],
//   開発_シニア: [
//     { name: '佐藤', age: 35, department: '開発', experience: 8 },
//     { name: '高橋', age: 29, department: '開発', experience: 5 }
//   ],
//   営業_ベテラン: [{ name: '鈴木', age: 42, department: '営業', experience: 15 }]
// }

ネストしたオブジェクトからのグルーピング

深い階層のプロパティを使ったグルーピングも、Lodash なら簡潔に書けます。

javascriptconst users = [
  {
    id: 1,
    profile: {
      name: '田中太郎',
      address: { prefecture: '東京都', city: '渋谷区' },
      preferences: { theme: 'dark', language: 'ja' },
    },
  },
  {
    id: 2,
    profile: {
      name: '佐藤花子',
      address: { prefecture: '大阪府', city: '大阪市' },
      preferences: { theme: 'light', language: 'ja' },
    },
  },
  {
    id: 3,
    profile: {
      name: 'John Smith',
      address: { prefecture: '東京都', city: '新宿区' },
      preferences: { theme: 'dark', language: 'en' },
    },
  },
];

// ドット記法でネストしたプロパティを指定
const groupedByPrefecture = groupBy(
  users,
  'profile.address.prefecture'
);

console.log(groupedByPrefecture);
// {
//   東京都: [
//     { id: 1, profile: { ... } },
//     { id: 3, profile: { ... } }
//   ],
//   大阪府: [
//     { id: 2, profile: { ... } }
//   ]
// }

さらに複雑な条件でのグルーピングも可能です。

javascript// テーマと言語の組み合わせでグルーピング
const groupedByThemeAndLang = groupBy(
  users,
  (user) =>
    `${user.profile.preferences.theme}_${user.profile.preferences.language}`
);

console.log(groupedByThemeAndLang);
// {
//   dark_ja: [{ id: 1, profile: { ... } }],
//   light_ja: [{ id: 2, profile: { ... } }],
//   dark_en: [{ id: 3, profile: { ... } }]
// }

以下の図は、ネストしたオブジェクトからのグルーピング処理の流れを示しています。

mermaidflowchart TD
    A[ネストしたオブジェクト配列] --> B{アクセス方法}
    B -->|ドット記法| C["'profile.address.prefecture'"]
    B -->|関数指定| D["user => user.profile.theme"]
    B -->|複合条件| E["複数プロパティの組み合わせ"]

    C --> F[シンプルなプロパティアクセス]
    D --> G[条件分岐やカスタム処理]
    E --> H[複雑な条件での分類]

    F --> I[グルーピング結果]
    G --> I
    H --> I

このように、Lodash groupBy を使うことで、複雑なデータ構造からでも直感的で読みやすいコードでグルーピング処理を実現できるのです。

具体例

ユーザーデータの年齢別グルーピング

実際のユーザー管理システムでよく見られる、年齢別のユーザーグルーピング処理を実装してみましょう。

まず、サンプルデータを準備します。

javascriptconst users = [
  {
    id: 1,
    name: '田中太郎',
    age: 23,
    email: 'tanaka@example.com',
    joinDate: '2023-04-01',
  },
  {
    id: 2,
    name: '佐藤花子',
    age: 28,
    email: 'sato@example.com',
    joinDate: '2022-08-15',
  },
  {
    id: 3,
    name: '鈴木一郎',
    age: 35,
    email: 'suzuki@example.com',
    joinDate: '2021-02-20',
  },
  {
    id: 4,
    name: '高橋美和',
    age: 29,
    email: 'takahashi@example.com',
    joinDate: '2023-01-10',
  },
  {
    id: 5,
    name: '渡辺健太',
    age: 42,
    email: 'watanabe@example.com',
    joinDate: '2020-06-30',
  },
  {
    id: 6,
    name: '山田由美',
    age: 26,
    email: 'yamada@example.com',
    joinDate: '2022-11-05',
  },
];

年代別にユーザーをグルーピングする処理を実装します。

javascriptimport { groupBy } from 'lodash';

// 年代別グルーピング関数
const getAgeGroup = (age) => {
  if (age < 25) return '20代前半';
  if (age < 30) return '20代後半';
  if (age < 35) return '30代前半';
  if (age < 40) return '30代後半';
  return '40代以上';
};

// 年代別にユーザーをグルーピング
const usersByAgeGroup = groupBy(users, (user) =>
  getAgeGroup(user.age)
);

console.log(usersByAgeGroup);
// {
//   '20代前半': [
//     { id: 1, name: '田中太郎', age: 23, ... }
//   ],
//   '20代後半': [
//     { id: 2, name: '佐藤花子', age: 28, ... },
//     { id: 4, name: '高橋美和', age: 29, ... },
//     { id: 6, name: '山田由美', age: 26, ... }
//   ],
//   '30代前半': [
//     { id: 3, name: '鈴木一郎', age: 35, ... }
//   ],
//   '40代以上': [
//     { id: 5, name: '渡辺健太', age: 42, ... }
//   ]
// }

さらに、グルーピング結果を統計情報として活用する処理も追加してみましょう。

javascript// 年代別統計情報を生成
const getAgeGroupStatistics = (groupedUsers) => {
  const statistics = {};

  Object.keys(groupedUsers).forEach((ageGroup) => {
    const usersInGroup = groupedUsers[ageGroup];
    statistics[ageGroup] = {
      count: usersInGroup.length,
      averageAge: Math.round(
        usersInGroup.reduce(
          (sum, user) => sum + user.age,
          0
        ) / usersInGroup.length
      ),
      userNames: usersInGroup.map((user) => user.name),
    };
  });

  return statistics;
};

const ageStatistics =
  getAgeGroupStatistics(usersByAgeGroup);
console.log(ageStatistics);
// {
//   '20代前半': { count: 1, averageAge: 23, userNames: ['田中太郎'] },
//   '20代後半': { count: 3, averageAge: 28, userNames: ['佐藤花子', '高橋美和', '山田由美'] },
//   '30代前半': { count: 1, averageAge: 35, userNames: ['鈴木一郎'] },
//   '40代以上': { count: 1, averageAge: 42, userNames: ['渡辺健太'] }
// }

商品データのカテゴリ別集計

EC サイトでよく使われる商品データの集計処理を実装してみます。

javascriptconst products = [
  {
    id: 1,
    name: 'iPhone 15',
    category: 'smartphone',
    price: 128000,
    stock: 50,
    brand: 'Apple',
  },
  {
    id: 2,
    name: 'MacBook Air',
    category: 'laptop',
    price: 148000,
    stock: 30,
    brand: 'Apple',
  },
  {
    id: 3,
    name: 'Galaxy S24',
    category: 'smartphone',
    price: 118000,
    stock: 25,
    brand: 'Samsung',
  },
  {
    id: 4,
    name: 'ThinkPad X1',
    category: 'laptop',
    price: 180000,
    stock: 15,
    brand: 'Lenovo',
  },
  {
    id: 5,
    name: 'iPad Pro',
    category: 'tablet',
    price: 98000,
    stock: 40,
    brand: 'Apple',
  },
  {
    id: 6,
    name: 'Surface Pro',
    category: 'tablet',
    price: 135000,
    stock: 20,
    brand: 'Microsoft',
  },
];

// カテゴリ別にグルーピング
const productsByCategory = groupBy(products, 'category');

console.log(productsByCategory);
// {
//   smartphone: [
//     { id: 1, name: 'iPhone 15', ... },
//     { id: 3, name: 'Galaxy S24', ... }
//   ],
//   laptop: [
//     { id: 2, name: 'MacBook Air', ... },
//     { id: 4, name: 'ThinkPad X1', ... }
//   ],
//   tablet: [
//     { id: 5, name: 'iPad Pro', ... },
//     { id: 6, name: 'Surface Pro', ... }
//   ]
// }

価格帯別のグルーピングも実装してみましょう。

javascript// 価格帯別グルーピング
const getPriceRange = (price) => {
  if (price < 100000) return '10万円未満';
  if (price < 150000) return '10-15万円';
  return '15万円以上';
};

const productsByPriceRange = groupBy(products, (product) =>
  getPriceRange(product.price)
);

// カテゴリ別売上予測計算
const getCategorySalesData = (groupedProducts) => {
  const salesData = {};

  Object.keys(groupedProducts).forEach((category) => {
    const categoryProducts = groupedProducts[category];
    const totalValue = categoryProducts.reduce(
      (sum, product) => sum + product.price * product.stock,
      0
    );
    const averagePrice = Math.round(
      categoryProducts.reduce(
        (sum, product) => sum + product.price,
        0
      ) / categoryProducts.length
    );

    salesData[category] = {
      productCount: categoryProducts.length,
      totalStock: categoryProducts.reduce(
        (sum, product) => sum + product.stock,
        0
      ),
      averagePrice: averagePrice,
      potentialRevenue: totalValue,
      brands: [
        ...new Set(
          categoryProducts.map((product) => product.brand)
        ),
      ],
    };
  });

  return salesData;
};

const categorySalesData = getCategorySalesData(
  productsByCategory
);
console.log(categorySalesData);
// {
//   smartphone: {
//     productCount: 2,
//     totalStock: 75,
//     averagePrice: 123000,
//     potentialRevenue: 9350000,
//     brands: ['Apple', 'Samsung']
//   },
//   ...
// }

ログデータの日付別集計

システム運用で重要なログデータの分析例を見てみましょう。

javascriptconst logs = [
  {
    timestamp: '2024-01-15 09:30:15',
    level: 'INFO',
    message: 'User login successful',
    userId: 'user001',
  },
  {
    timestamp: '2024-01-15 10:45:22',
    level: 'ERROR',
    message: 'Database connection failed',
    userId: null,
  },
  {
    timestamp: '2024-01-15 11:20:33',
    level: 'WARN',
    message: 'High memory usage detected',
    userId: null,
  },
  {
    timestamp: '2024-01-16 08:15:45',
    level: 'INFO',
    message: 'User login successful',
    userId: 'user002',
  },
  {
    timestamp: '2024-01-16 09:30:12',
    level: 'ERROR',
    message: 'API timeout occurred',
    userId: 'user001',
  },
  {
    timestamp: '2024-01-16 14:22:55',
    level: 'INFO',
    message: 'File upload completed',
    userId: 'user003',
  },
  {
    timestamp: '2024-01-17 07:45:30',
    level: 'ERROR',
    message: 'Authentication failed',
    userId: null,
  },
];

// 日付別にログをグルーピング
const logsByDate = groupBy(
  logs,
  (log) => log.timestamp.split(' ')[0]
);

console.log(logsByDate);
// {
//   '2024-01-15': [...],
//   '2024-01-16': [...],
//   '2024-01-17': [...]
// }

ログレベル別の集計も実装してみます。

javascript// ログレベル別グルーピング
const logsByLevel = groupBy(logs, 'level');

// 日付とレベルの組み合わせでグルーピング
const logsByDateAndLevel = groupBy(logs, (log) => {
  const date = log.timestamp.split(' ')[0];
  return `${date}_${log.level}`;
});

// ログ分析レポート生成
const generateLogReport = (logs) => {
  const byDate = groupBy(
    logs,
    (log) => log.timestamp.split(' ')[0]
  );
  const report = {};

  Object.keys(byDate).forEach((date) => {
    const dayLogs = byDate[date];
    const byLevel = groupBy(dayLogs, 'level');

    report[date] = {
      total: dayLogs.length,
      levels: {
        INFO: (byLevel.INFO || []).length,
        WARN: (byLevel.WARN || []).length,
        ERROR: (byLevel.ERROR || []).length,
      },
      errorRate:
        (
          ((byLevel.ERROR || []).length / dayLogs.length) *
          100
        ).toFixed(2) + '%',
      uniqueUsers: new Set(
        dayLogs
          .filter((log) => log.userId)
          .map((log) => log.userId)
      ).size,
    };
  });

  return report;
};

const logReport = generateLogReport(logs);
console.log(logReport);
// {
//   '2024-01-15': {
//     total: 3,
//     levels: { INFO: 1, WARN: 1, ERROR: 1 },
//     errorRate: '33.33%',
//     uniqueUsers: 1
//   },
//   ...
// }

複数キーでの複合グルーピング

より高度な例として、複数の条件を組み合わせた複合グルーピングを実装してみましょう。

javascriptconst salesData = [
  {
    date: '2024-01-15',
    region: '関東',
    product: 'スマートフォン',
    amount: 150000,
    salesperson: '田中',
  },
  {
    date: '2024-01-15',
    region: '関西',
    product: 'ノートPC',
    amount: 200000,
    salesperson: '佐藤',
  },
  {
    date: '2024-01-16',
    region: '関東',
    product: 'スマートフォン',
    amount: 180000,
    salesperson: '鈴木',
  },
  {
    date: '2024-01-16',
    region: '関西',
    product: 'タブレット',
    amount: 120000,
    salesperson: '田中',
  },
  {
    date: '2024-01-17',
    region: '関東',
    product: 'ノートPC',
    amount: 250000,
    salesperson: '佐藤',
  },
  {
    date: '2024-01-17',
    region: '関西',
    product: 'スマートフォン',
    amount: 160000,
    salesperson: '鈴木',
  },
];

// 地域と商品の組み合わせでグルーピング
const salesByRegionAndProduct = groupBy(
  salesData,
  (sale) => `${sale.region}_${sale.product}`
);

// 日付と地域でグルーピング
const salesByDateAndRegion = groupBy(
  salesData,
  (sale) => `${sale.date}_${sale.region}`
);

// 営業担当者別の成績分析
const analyzeSalesPerformance = (salesData) => {
  const bySalesperson = groupBy(salesData, 'salesperson');
  const performance = {};

  Object.keys(bySalesperson).forEach((person) => {
    const personSales = bySalesperson[person];
    const byRegion = groupBy(personSales, 'region');
    const byProduct = groupBy(personSales, 'product');

    performance[person] = {
      totalSales: personSales.reduce(
        (sum, sale) => sum + sale.amount,
        0
      ),
      salesCount: personSales.length,
      averageSale: Math.round(
        personSales.reduce(
          (sum, sale) => sum + sale.amount,
          0
        ) / personSales.length
      ),
      regions: Object.keys(byRegion),
      products: Object.keys(byProduct),
      regionBreakdown: Object.keys(byRegion).reduce(
        (acc, region) => {
          acc[region] = byRegion[region].reduce(
            (sum, sale) => sum + sale.amount,
            0
          );
          return acc;
        },
        {}
      ),
    };
  });

  return performance;
};

const performanceReport =
  analyzeSalesPerformance(salesData);
console.log(performanceReport);
// {
//   田中: {
//     totalSales: 270000,
//     salesCount: 2,
//     averageSale: 135000,
//     regions: ['関東', '関西'],
//     products: ['スマートフォン', 'タブレット'],
//     regionBreakdown: { 関東: 150000, 関西: 120000 }
//   },
//   ...
// }

以下の図は、複合グルーピングの処理フローを示しています。

mermaidflowchart TD
    A[売上データ配列] --> B{グルーピング条件}
    B -->|地域×商品| C[地域商品別分析]
    B -->|日付×地域| D[期間地域別分析]
    B -->|営業担当者| E[個人成績分析]

    C --> F[マーケット分析]
    D --> G[トレンド分析]
    E --> H[人事評価]

    F --> I[ビジネス戦略]
    G --> I
    H --> I

これらの具体例を通して、Lodash の groupBy がいかに強力で実用的な機能かを理解していただけたでしょうか。シンプルな構文でありながら、非常に柔軟で高度なデータグルーピングが可能になるのです。

まとめ

Lodash の groupBy 関数は、JavaScript でのデータグルーピング処理を劇的に簡潔にしてくれる非常に強力なツールです。

本記事で解説した主なポイントをまとめますと:

背景として理解すべきこと

  • 現代の Web 開発では配列データのグルーピングが頻繁に必要
  • 生の JavaScript での実装は複雑で保守性に課題がある
  • Lodash groupBy により簡潔で読みやすいコードが実現可能

解決される主要課題

  • 複雑なデータ構造からの効率的なグルーピング処理
  • 大量データでのパフォーマンス問題の解決
  • チーム開発での可読性と保守性の向上

実装における重要なポイント

  • 文字列指定による簡単なプロパティグルーピング
  • 関数を使った動的で柔軟な条件指定
  • ドット記法によるネストしたプロパティへのアクセス
  • 複数条件を組み合わせた高度なグルーピング

実用的な活用場面

  • ユーザー管理システムでの属性別分析
  • EC サイトでの商品カテゴリ・価格帯別集計
  • システムログの日付・レベル別分析
  • 売上データの多次元集計と分析

Lodash groupBy を使いこなすことで、データ処理の効率性と可読性を大幅に向上させることができます。特に、React や Vue.js などのモダンなフロントエンド開発において、API から取得したデータを効率的に加工・表示する際に威力を発揮するでしょう。

今回紹介した基本的な使い方から応用例まで、ぜひ実際のプロジェクトで試してみてください。きっと、データ処理がこれまでよりもスムーズで楽しくなるはずです。

関連リンク