データの集合を特定の条件(キー)ごとにグループ化したい場合、LINQのToLookupメソッドを使用すると非常に便利です。
よく似た機能にGroupByがありますが、ToLookupは即時実行され、扱いやすいILookup<TKey, TElement>インターフェースを返すという特徴があります。今回は、社内の従業員リストを部署ごとにグループ化するシナリオを通じて、ToLookupの基本的な使い方と特性について解説します。
ToLookupメソッドの概要
ToLookupメソッドは、シーケンス(配列やリストなど)の各要素に対してキーを選択し、そのキーに基づいて要素をグループ化したILookup<TKey, TElement>を作成します。
一般的なDictionary<TKey, TValue>が一つのキーに対して一つの値を持つのに対し、ILookupは一つのキーに対して複数の値(コレクション)を持つことができます。これは「1対多」の関係を表現するのに最適です。
実践的なコード例:部署ごとの従業員リスト作成
以下のコードは、従業員データ(名前と部署名)のリストから、部署をキーとして従業員名のリストを引けるルックアップテーブルを作成する例です。
using System;
using System.Collections.Generic;
using System.Linq;
namespace EmployeeManagement
{
// 従業員を表すレコード
public record Employee(string Name, string Department);
class Program
{
static void Main()
{
// 従業員データのリスト
var employees = new[]
{
new Employee("佐藤", "Sales"),
new Employee("田中", "Engineering"),
new Employee("鈴木", "Sales"),
new Employee("高橋", "Engineering"),
new Employee("伊藤", "Human Resources"),
new Employee("渡辺", "Sales")
};
// 1. ToLookupを使用してグループ化
// 第1引数: グループ化のキー(部署)
// 第2引数: 値として格納する要素(名前)
ILookup<string, string> employeesByDept =
employees.ToLookup(e => e.Department, e => e.Name);
// 2. グループごとのデータを出力
foreach (IGrouping<string, string> group in employeesByDept)
{
Console.WriteLine($"# 部署: {group.Key}");
foreach (var name in group)
{
Console.WriteLine($" - {name}");
}
}
// 3. 特定のキー(部署)を指定して直接アクセスすることも可能
Console.WriteLine("\n--- Engineering部のメンバー ---");
foreach (var engineer in employeesByDept["Engineering"])
{
Console.WriteLine($" - {engineer}");
}
}
}
}
実行結果
Plaintext
# 部署: Sales
- 佐藤
- 鈴木
- 渡辺
# 部署: Engineering
- 田中
- 高橋
# 部署: Human Resources
- 伊藤
--- Engineering部のメンバー ---
- 田中
- 高橋
技術的なポイントとGroupByとの違い
1. キーセレクタと要素セレクタ
ToLookupのオーバーロードでは、第1引数に「何をキーにするか(KeySelector)」、第2引数に「値を何にするか(ElementSelector)」を指定できます。上記の例では、e => e.Nameを指定しているため、ルックアップの値はEmployeeオブジェクトそのものではなく、string型の名前になります。これにより、必要なデータだけを抽出した軽量な構造を作成できます。
2. 存在しないキーへのアクセス安全性
Dictionaryの場合、存在しないキーにインデクサでアクセスすると例外が発生しますが、ILookupの場合は空のシーケンスを返します。そのため、事前にキーの存在確認(ContainsKeyなど)を行う必要がなく、コードをシンプルに記述できます。
3. 即時実行と遅延実行
これがGroupByとの最大の違いです。
- GroupBy: 遅延実行(Lazy Evaluation)されます。列挙(foreachなど)するまで処理は行われません。
- ToLookup: 即時実行(Immediate Execution)されます。メソッドが呼ばれた時点で全てのデータを読み込み、メモリ上にデータ構造を構築します。
データソースがデータベースや外部APIである場合や、結果をキャッシュとして繰り返し利用したい場合は、ToLookupを使用してデータをメモリ上に確定させるアプローチが有効です。
まとめ
ToLookupを使用すると、フラットなリストデータを簡単に階層構造(キーと値のコレクション)に変換できます。「キーが存在しない場合でもエラーにならず空の結果を返す」という特性は、検索ロジックを実装する際に非常に扱いやすいものです。データの集計や分類が必要な場面で、ぜひ活用してみてください。
