C# の LINQ で最大・最小を取る Max と Min
Max と Min はシーケンスの最大値・最小値を取得するメソッドだ。数値だけでなく、比較可能なあらゆる型に使える。
基本的な使い方
数値の最大値と最小値を取得する。
int[] numbers = { 3, 1, 4, 1, 5, 9, 2, 6 };
int max = numbers.Max();
int min = numbers.Min();
Console.WriteLine($"Max: {max}"); // Max: 9
Console.WriteLine($"Min: {min}"); // Min: 1シンプルで直感的だ。
オブジェクトのプロパティで取得
セレクタを指定すれば、特定のプロパティの最大・最小を取得できる。
var products = new List<Product>
{
new Product { Name = "Apple", Price = 100 },
new Product { Name = "Banana", Price = 80 },
new Product { Name = "Cherry", Price = 200 }
};
int maxPrice = products.Max(p => p.Price);
int minPrice = products.Min(p => p.Price);
Console.WriteLine($"最高価格: {maxPrice}"); // 最高価格: 200
Console.WriteLine($"最安価格: {minPrice}"); // 最安価格: 80価格の最大・最小が得られる。
最大・最小の要素を取得する
値ではなく、その値を持つ要素自体が欲しい場合がある。単純に Max を使うと値しか得られない。
var products = new List<Product>
{
new Product { Name = "Apple", Price = 100 },
new Product { Name = "Banana", Price = 80 },
new Product { Name = "Cherry", Price = 200 }
};
// これは価格の最大値(200)だけ
int maxPrice = products.Max(p => p.Price);
// 最高価格の商品を取得するには OrderBy を使う
var mostExpensive = products.OrderByDescending(p => p.Price).First();
Console.WriteLine(mostExpensive.Name); // Cherry.NET 6 以降では MaxBy / MinBy が使える。
// .NET 6 以降
var mostExpensive = products.MaxBy(p => p.Price);
var cheapest = products.MinBy(p => p.Price);
Console.WriteLine(mostExpensive.Name); // Cherry
Console.WriteLine(cheapest.Name); // Banana空のシーケンスに注意
空のシーケンスに対して Max / Min を呼ぶと例外が発生する。
var empty = new List<int>();
// int max = empty.Max(); // InvalidOperationException対処法はいくつかある。
var list = new List<int>();
// 方法1:事前チェック
int? max1 = list.Any() ? list.Max() : (int?)null;
// 方法2:DefaultIfEmpty
int max2 = list.DefaultIfEmpty(0).Max(); // デフォルト値を指定
// 方法3:Nullable 型を使う(.NET 6以降)
int? max3 = list.Select(x => (int?)x).Max();DefaultIfEmpty を使う方法が最もシンプルだ。
文字列の最大・最小
文字列でも使える。辞書順(アルファベット順)で比較される。
string[] names = { "Charlie", "Alice", "Bob" };
string max = names.Max(); // Charlie
string min = names.Min(); // Alice大文字は小文字より前に来ることに注意。カスタムの比較が必要なら MaxBy と MinBy を使う。
日付の最大・最小
DateTime にも適用できる。最新の日付や最古の日付を取得するのに便利だ。
var events = new List<Event>
{
new Event { Name = "Meeting", Date = new DateTime(2024, 1, 15) },
new Event { Name = "Conference", Date = new DateTime(2024, 3, 20) },
new Event { Name = "Workshop", Date = new DateTime(2024, 2, 10) }
};
DateTime latest = events.Max(e => e.Date);
DateTime earliest = events.Min(e => e.Date);
Console.WriteLine($"最新: {latest:yyyy/MM/dd}"); // 最新: 2024/03/20
Console.WriteLine($"最古: {earliest:yyyy/MM/dd}"); // 最古: 2024/01/15Nullable 型での動作
int? のような Nullable 型では、null は無視される。
int?[] numbers = { 3, null, 7, null, 2 };
int? max = numbers.Max(); // 7
int? min = numbers.Min(); // 2すべてが null の場合、結果も null になる。
int?[] allNull = { null, null, null };
int? max = allNull.Max(); // null(例外は発生しない)GroupBy との組み合わせ
グループごとの最大・最小を求めるパターン。
var scores = new List<Score>
{
new Score { Subject = "Math", Value = 80 },
new Score { Subject = "English", Value = 90 },
new Score { Subject = "Math", Value = 95 },
new Score { Subject = "English", Value = 85 }
};
var result = scores
.GroupBy(s => s.Subject)
.Select(g => new
{
Subject = g.Key,
Max = g.Max(s => s.Value),
Min = g.Min(s => s.Value)
});
foreach (var item in result)
{
Console.WriteLine($"{item.Subject}: 最高{item.Max}, 最低{item.Min}");
}
// Math: 最高95, 最低80
// English: 最高90, 最低85科目ごとの最高点・最低点を集計している。SQL の GROUP BY と MAX / MIN の組み合わせに相当する処理だ。