はじめに:「辞書変換」は“検索しやすい形に並び替える技”
業務でよくあるのが、
社員番号から社員情報をすぐ取りたい
商品コードから商品情報を一発で引きたい
部署コードから部署名をすぐ知りたい
こういう「キーから素早く引きたい」場面です。
このときに力を発揮するのが Dictionary<TKey, TValue>、そして LINQ の ToDictionary です。
ここでは、初心者向けに
キーと値の基本ToDictionary の使い方
重複キー・null への注意
実務でよく使う辞書変換ユーティリティ
をかみ砕いて説明していきます。
基本:List を Dictionary に変換するイメージ
「列」から「キーで引けるマップ」へ
List<T> は「順番に並んだデータの集まり」です。
一方 Dictionary<TKey, TValue> は、「キー → 値」を素早く引ける“マップ”です。
イメージとしては、
社員の一覧(List<Employee>)
↓
社員番号 → 社員情報(Dictionary<int, Employee>)
という変換をする感じです。
LINQ の ToDictionary は、この変換を一行で書くためのメソッドです。
ToDictionary の基本形
いちばんシンプルな形
まずは数値のリストを、「値そのものをキーにした辞書」にしてみます。
using System;
using System.Collections.Generic;
using System.Linq;
var numbers = new List<int> { 10, 20, 30 };
var dict = numbers.ToDictionary(x => x); // キーも値も x
Console.WriteLine(dict[10]); // 10
Console.WriteLine(dict[20]); // 20
C#ここでの重要ポイントは、「ToDictionary(x => x) は“要素そのものをキーにする”」という形だということです。
実務では、これより「プロパティをキーにする」パターンをよく使います。
オブジェクトを「ID → オブジェクト」の辞書にする
社員番号から社員を引ける辞書
社員クラスを用意します。
public class Employee
{
public int No { get; set; }
public string Name { get; set; } = "";
public string Department { get; set; } = "";
}
C#データを作ります。
var employees = new List<Employee>
{
new Employee { No = 1, Name = "Sato", Department = "Sales" },
new Employee { No = 2, Name = "Suzuki", Department = "Dev" },
new Employee { No = 3, Name = "Tanaka", Department = "Sales" },
};
C#社員番号をキーにした辞書に変換します。
var employeeMap = employees.ToDictionary(e => e.No);
C#これで、こう書けます。
var e2 = employeeMap[2];
Console.WriteLine(e2.Name); // Suzuki
C#ここでの重要ポイントは、「ToDictionary(e => e.No) で“No をキーに、Employee を値にした辞書”ができる」ことです。
値は元の要素そのもの(Employee)になります。
キーと値の両方を指定する ToDictionary
「コード → 名称」の辞書を作る
「部署コード → 部署名」のように、
値として“全部のオブジェクト”ではなく“特定のプロパティだけ”を持たせたいことも多いです。
部署クラスを用意します。
public class Department
{
public string Code { get; set; } = ""; // "SLS", "DEV" など
public string Name { get; set; } = ""; // "営業部", "開発部" など
}
C#var departments = new List<Department>
{
new Department { Code = "SLS", Name = "営業部" },
new Department { Code = "DEV", Name = "開発部" },
};
C#コード → 名称の辞書にします。
var deptNameMap = departments
.ToDictionary(d => d.Code, d => d.Name);
C#使い方はこうです。
Console.WriteLine(deptNameMap["SLS"]); // 営業部
C#ここでの重要ポイントは、「ToDictionary(keySelector, valueSelector) で“キーも値も自由に選べる”」ことです。keySelector がキー、valueSelector が辞書に格納される値です。
重複キーに注意する
同じキーがあると例外になる
ToDictionary は、「キーが重複している」と例外になります。
var list = new[] { "A", "B", "A" };
// これは例外(ArgumentException)
var dict = list.ToDictionary(x => x);
C#業務データでも、「同じ社員番号が二重に入っていた」「同じコードが重複していた」などは普通に起こり得ます。
ここでの重要ポイントは、「辞書に変換する前に“キーが一意かどうか”を意識する」ことです。
重複があり得るときのパターン
重複を許容したい場合は、いきなり ToDictionary ではなく、
「キーごとにグルーピングしてから辞書にする」という手があります。
var sales = new[]
{
new { Customer = "A", Amount = 1000 },
new { Customer = "A", Amount = 2000 },
new { Customer = "B", Amount = 1500 },
};
var salesByCustomer = sales
.GroupBy(x => x.Customer)
.ToDictionary(
g => g.Key,
g => g.ToList()); // 1顧客に複数明細
C#これで、Dictionary<string, List<...>> になります。
foreach (var s in salesByCustomer["A"])
{
Console.WriteLine(s.Amount); // 1000, 2000
}
C#「1キーに1件」なら ToDictionary、
「1キーに複数件」なら GroupBy → ToDictionary というイメージです。
null・キー欠損との付き合い方
キーが存在しないときの扱い
辞書から値を取り出すとき、存在しないキーを dict[key] で参照すると例外になります。
var e999 = employeeMap[999]; // KeyNotFoundException
C#安全に書くなら TryGetValue を使います。
if (employeeMap.TryGetValue(2, out var emp))
{
Console.WriteLine(emp.Name);
}
else
{
Console.WriteLine("社員が見つかりません");
}
C#ここでの重要ポイントは、「辞書からの取得は“必ずある前提”か、“ないかもしれない前提”かを決める」ことです。
ない可能性があるなら、TryGetValue を使う癖をつけておくと安全です。
null かもしれないコレクションから辞書を作る
List<Employee>? employees = GetEmployeesOrNull();
C#この場合、いきなり employees.ToDictionary(...) と書くと NullReferenceException です。
var employeeMap = (employees ?? Enumerable.Empty<Employee>())
.ToDictionary(e => e.No);
C#employees が null なら空列として扱い、そのまま空の辞書ができます。
実務でよく使う辞書変換ユーティリティ
安全な ToDictionary ラッパーを作る
「null でも空でも、とりあえず辞書にしたい」という場面は多いので、
拡張メソッドにしてしまうのも手です。
public static class DictionaryExtensions
{
public static Dictionary<TKey, TValue> ToSafeDictionary<TSource, TKey, TValue>(
this IEnumerable<TSource>? source,
Func<TSource, TKey> keySelector,
Func<TSource, TValue> valueSelector)
where TKey : notnull
{
if (source is null)
{
return new Dictionary<TKey, TValue>();
}
return source.ToDictionary(keySelector, valueSelector);
}
}
C#使い方はこうです。
List<Department>? departments = GetDepartmentsOrNull();
var deptNameMap = departments
.ToSafeDictionary(d => d.Code, d => d.Name);
C#ここでの重要ポイントは、「呼び出し側から null チェックを消して、“常に辞書が返る”契約にしている」ことです。
これで、後続のコードは「辞書は必ず存在する(中身が空のことはある)」前提で書けます。
実務で意識してほしいこと
「何をキーにするか」を日本語で先に決める
辞書変換を書く前に、必ず言葉で整理してください。
社員番号から社員を引きたい
部署コードから部署名を引きたい
顧客コードからその顧客の全明細を引きたい
これが決まれば、ToDictionary の形はほぼ自動的に決まります。
社員番号 → 社員ToDictionary(e => e.No)
部署コード → 部署名ToDictionary(d => d.Code, d => d.Name)
顧客コード → 明細一覧GroupBy(x => x.Customer).ToDictionary(g => g.Key, g => g.ToList())
という対応です。
「一意か」「複数か」を最初に決める
辞書変換で一番ハマりやすいのが「キー重複」です。
なので、最初にこう決めておくと安全です。
このキーは、必ず一意であるべきか?
同じキーに複数行あるのが普通か?
一意なら ToDictionary、
複数なら GroupBy → ToDictionary というパターンを使い分けましょう。
まとめ:「辞書変換ユーティリティ」は“業務データにインデックスを貼る道具”
辞書変換の本質は、
「順番で並んだデータを、“キーで一瞬で引ける形”に組み替える」
ことです。
押さえておきたいポイントは、
ToDictionary(keySelector) で「キー → 元オブジェクト」ToDictionary(keySelector, valueSelector) で「キー → 任意の値」
キー重複があると例外になるので、一意かどうかを意識する
複数行をぶら下げたいときは GroupBy → ToDictionary
null やキー欠損は ?? Enumerable.Empty<T>() や TryGetValue で安全に扱う
ここまで身につけば、「毎回 List を線形検索して探す」コードから卒業して、
“業務・実務で通用する、キーアクセス前提のデータ構造”を LINQ で自然に扱えるようになります。
