はじめに:「グルーピング」は“バラバラの明細を、かたまりにまとめる技”
業務システムでは、こんなことをよくやります。
「部署ごとに社員をまとめたい」
「顧客ごとに売上を集計したい」
「日付ごとにアクセス数を集計したい」
この「同じキーを持つデータを、ひとまとまりにする」のが、グルーピングです。
C# の LINQ では GroupBy を使うことで、これをとても自然に書けます。
ここでは、初心者向けに
- GroupBy の基本
- グループごとの集計(件数・合計・平均など)
- 実務でよくある「顧客別売上」「部署別社員一覧」
- グルーピング結果をユーティリティとして扱うコツ
まで、順番にかみ砕いて説明していきます。
GroupBy の基本:同じキーを持つものを「かたまり」にする
まずはシンプルな例で動きをつかむ
数値を「偶数グループ」と「奇数グループ」に分けてみます。
using System;
using System.Collections.Generic;
using System.Linq;
var numbers = new List<int> { 1, 2, 3, 4, 5, 6 };
var groups = numbers.GroupBy(x => x % 2 == 0 ? "Even" : "Odd");
foreach (var g in groups)
{
Console.WriteLine($"Key = {g.Key}");
foreach (var n in g)
{
Console.WriteLine($" {n}");
}
}
C#出力イメージはこんな感じです。
Key = Odd
1
3
5
Key = Even
2
4
6
ここでの重要ポイントは、「GroupBy の結果は“グループの集合”」だということです。
各グループは「Key(グループのキー)」と「そのキーに属する要素の列」を持っています。
g.Key がグループの名前(ここでは “Odd” / “Even”)、g 自体が「そのグループに属する要素の列」です。
オブジェクトを業務キーでグルーピングする
社員を「部署ごと」にまとめる
業務で一番よくあるのは、「部署ごと」「顧客ごと」「日付ごと」といったグルーピングです。
社員クラスを用意します。
public class Employee
{
public int No { get; set; }
public string Name { get; set; } = "";
public string Department { get; set; } = ""; // "Sales", "Dev", ...
}
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" },
new Employee { No = 4, Name = "Yamada", Department = "Dev" },
};
C#部署ごとにグルーピングしてみます。
var groups = employees.GroupBy(e => e.Department);
foreach (var g in groups)
{
Console.WriteLine($"Department = {g.Key}");
foreach (var e in g)
{
Console.WriteLine($" {e.No}: {e.Name}");
}
}
C#出力イメージはこうです。
Department = Sales
1: Sato
3: Tanaka
Department = Dev
2: Suzuki
4: Yamada
ここでの重要ポイントは、「GroupBy(e => e.Department) で“部署ごとに社員をまとめる”」という意味になることです。g.Key が部署コード、g の中身がその部署の社員一覧です。
グループごとの集計:件数・合計・平均など
部署ごとの社員数を出す
グルーピングの次の一歩は、「グループごとに集計する」ことです。
例えば「部署ごとの社員数」を出してみます。
var result = employees
.GroupBy(e => e.Department)
.Select(g => new
{
Department = g.Key,
Count = g.Count()
});
foreach (var x in result)
{
Console.WriteLine($"{x.Department}: {x.Count} 人");
}
C#ここでの重要ポイントは、「GroupBy のあとに Select で“グループ単位の結果”を作る」ことです。g.Count() は「そのグループに属する要素の件数」を返します。
顧客ごとの売上合計を出す
もう少し実務っぽい例として、「顧客ごとの売上合計」を考えてみます。
public class Sale
{
public string Customer { get; set; } = "";
public DateTime Date { get; set; }
public int Amount { get; set; }
}
C#データを用意します。
var sales = new List<Sale>
{
new Sale { Customer = "A", Date = new DateTime(2026, 2, 10), Amount = 1000 },
new Sale { Customer = "B", Date = new DateTime(2026, 2, 10), Amount = 2000 },
new Sale { Customer = "A", Date = new DateTime(2026, 2, 11), Amount = 3000 },
};
C#顧客ごとの合計金額を出します。
var summary = sales
.GroupBy(s => s.Customer)
.Select(g => new
{
Customer = g.Key,
Total = g.Sum(x => x.Amount),
Count = g.Count()
});
foreach (var x in summary)
{
Console.WriteLine($"{x.Customer}: 合計 {x.Total} 円 / {x.Count} 件");
}
C#ここでの重要ポイントは、「g.Sum(x => x.Amount) のように、グループ内の要素に対してさらに LINQ を使える」ことです。Count, Sum, Average, Min, Max など、集計系メソッドはグルーピングと相性抜群です。
日付ごとのグルーピング:日別集計の定番パターン
DateTime を「日付だけ」でグルーピングする
売上やアクセスログなどでは、「日付ごとの件数・合計」を出したいことが多いです。
var daily = sales
.GroupBy(s => s.Date.Date) // 時刻を切り捨てて“日付だけ”でグルーピング
.Select(g => new
{
Date = g.Key,
Total = g.Sum(x => x.Amount)
})
.OrderBy(x => x.Date);
C#DateTime.Date は「時刻部分を 00:00:00 にした日付」を返すので、
同じ日付のデータが同じグループにまとまります。
ここでの重要ポイントは、「グルーピングのキーを“どう切り出すか”が設計の肝」だということです。
日付単位なら s.Date.Date、月単位なら new { s.Date.Year, s.Date.Month } のように、
“まとめたい単位”をキーとして渡します。
複合キーでグルーピングする
「顧客 × 年月」ごとの売上集計
例えば、「顧客ごと・月ごとの売上」を出したいとします。
このときのキーは「顧客」と「年月」の組み合わせです。
var monthly = sales
.GroupBy(s => new { s.Customer, s.Date.Year, s.Date.Month })
.Select(g => new
{
g.Key.Customer,
g.Key.Year,
g.Key.Month,
Total = g.Sum(x => x.Amount)
})
.OrderBy(x => x.Customer)
.ThenBy(x => x.Year)
.ThenBy(x => x.Month);
C#ここでの重要ポイントは、「匿名型 new { ... } をキーに使うと、複数項目をまとめてグルーピングできる」ことです。g.Key の中に、Customer, Year, Month が全部入っています。
グルーピング結果を“辞書っぽく”扱う
ToDictionary で「キー → グループ」を引けるようにする
「部署コードを指定したら、その部署の社員一覧を取りたい」というようなとき、
グルーピング結果を Dictionary にしておくと便利です。
var map = employees
.GroupBy(e => e.Department)
.ToDictionary(g => g.Key, g => g.ToList());
C#これで、map["Sales"] で「営業部の社員一覧」が取れるようになります。
if (map.TryGetValue("Sales", out var salesEmployees))
{
foreach (var e in salesEmployees)
{
Console.WriteLine($"{e.No}: {e.Name}");
}
}
C#ここでの重要ポイントは、「GroupBy → ToDictionary で“キーからグループを引けるマップ”を作れる」ことです。
頻繁に参照する場合は、この形にしておくとアクセスが楽になります。
実務で意識してほしいこと
「何でまとめたいのか」を日本語で先に決める
グルーピングを書く前に、必ず言葉で整理してください。
部署ごとに社員をまとめたい
顧客ごとに売上をまとめたい
日付ごとにアクセス数を集計したい
顧客 × 年月ごとに売上を集計したい
これが決まれば、GroupBy のキーはほぼ自動的に決まります。
部署ごと → GroupBy(e => e.Department)
顧客ごと → GroupBy(s => s.Customer)
日付ごと → GroupBy(s => s.Date.Date)
顧客 × 年月 → GroupBy(s => new { s.Customer, s.Date.Year, s.Date.Month })
という具合です。
「グルーピング → 集計 → ソート」の流れを意識する
一覧やレポートでは、だいたいこの流れになります。
グルーピングする(GroupBy)
グループごとの集計結果を作る(Select)
表示しやすい順に並べる(OrderBy / ThenBy)
順番を意識して書くと、コードの意図がとても読みやすくなります。
まとめ:「グルーピングユーティリティ」は“明細を意味のあるかたまりに変える道具”
グルーピングの本質は、
「バラバラの明細を、“同じ意味を持つもの同士”でまとめて、
グループ単位で考えられるようにする」
ことです。
押さえておきたいポイントは、
GroupBy(キー) で「キーごとのグループ」を作る
グループごとの集計は Select(g => new { g.Key, ... g.Count(), g.Sum(...) ... }) の形で書く
複合キーは new { ... } を使うGroupBy → ToDictionary で「キー → グループ」のマップも作れる
ここまで身につけば、「1件ずつ for で回して自前で集計する」スタイルから卒業して、
“LINQ らしい、読みやすいグルーピング&集計”を業務コードに自然に組み込めるようになります。
