C# Tips | コレクション・LINQ:最小値取得

C# C#
スポンサーリンク

はじめに:「最小値取得」は“いちばん小さい値を素早く見つける技”

業務システムでは、最大値と同じくらい「最小値」もよく使います。

最も安い商品価格
最も古い日付(最初の処理日)
最も少ない数量
最も短い対応時間

こうした「いちばん小さい値」を取り出す処理は、C# / LINQ の Min を使うととても簡単に書けます。

ここでは、初心者向けに

  • 数値の最小値
  • オブジェクトのプロパティの最小値
  • 空コレクション・null との付き合い方
  • GroupBy と組み合わせた「○○ごとの最小値」

を順番にかみ砕いて説明していきます。


基本:数値コレクションに対する Min

int の最小値を取得する

まずは一番シンプルな例です。

using System;
using System.Collections.Generic;
using System.Linq;

var numbers = new List<int> { 5, 2, 9, 1, 3 };

int min = numbers.Min();

Console.WriteLine(min); // 1
C#

Min() は「列の中でいちばん小さい値」を返します。

ここでの重要ポイントは、「Min は“値そのもの”を返す」ということです。
最大値と同じく、最小値を持つ“要素そのもの”ではなく、値だけが返ってきます。

double や decimal の最小値

var values = new List<double> { 1.2, 3.4, 0.8 };

double min = values.Min(); // 0.8
C#
var prices = new List<decimal> { 100.5m, 80.0m, 120.0m };

decimal minPrice = prices.Min(); // 80.0
C#

金額などは decimal を使うことが多いので、そのまま Min が使えます。


オブジェクトのプロパティ最小値:Min(selector)

売上明細の「最小金額」を取得する

業務では、単純な数値のリストよりも、
「オブジェクトの中の特定のプロパティの最小値」を知りたいことが多いです。

public class Sale
{
    public string Item { get; set; } = "";
    public int Quantity { get; set; }
    public int UnitPrice { get; set; }

    public int Amount => Quantity * UnitPrice;
}
C#
var sales = new List<Sale>
{
    new Sale { Item = "A", Quantity = 2, UnitPrice = 1000 }, // 2000
    new Sale { Item = "B", Quantity = 1, UnitPrice = 5000 }, // 5000
    new Sale { Item = "C", Quantity = 3, UnitPrice = 1500 }, // 4500
};
C#

金額の最小値を取得します。

int minAmount = sales.Min(s => s.Amount);

Console.WriteLine(minAmount); // 2000
C#

ここでの重要ポイントは、「Min(s => s.Amount) の中で“何を基準に最小値を取るか”を指定している」ことです。

日付の最小値(最古の日付)を取得する

public class EventLog
{
    public DateTime OccurredAt { get; set; }
}
C#
var logs = new List<EventLog>
{
    new EventLog { OccurredAt = new DateTime(2026, 2, 12) },
    new EventLog { OccurredAt = new DateTime(2026, 2, 10) },
    new EventLog { OccurredAt = new DateTime(2026, 2, 11) },
};

DateTime earliest = logs.Min(x => x.OccurredAt);

Console.WriteLine(earliest); // 2026-02-10
C#

DateTime も「小さいほうが古い日付」なので、Min で「最古」を取れます。


空コレクション・null との付き合い方

空コレクションに対する Min は例外になる

Min は「空のコレクション」に対して呼ぶと例外になります。

var empty = new List<int>();

int min = empty.Min(); // InvalidOperationException
C#

「最小値」という概念は、要素が1つもないと定義できないためです。

ここでの重要ポイントは、「Min を呼ぶ前に Any() で“1件以上あるか”を確認する」ことです。

int? min = null;

if (numbers.Any())
{
    min = numbers.Min();
}
C#

null かもしれないコレクション

List<int>? numbers = GetNumbersOrNull();
C#

この場合も、いきなり numbers.Min() と書くと NullReferenceException です。

int? min = null;

if (numbers != null && numbers.Any())
{
    min = numbers.Min();
}
C#

業務的に「データなしなら 0 扱い」などのルールがある場合は、?? 0 を使います。

null 許容型(int? など)の最小値

public class Score
{
    public string Name { get; set; } = "";
    public int? Value { get; set; }
}
C#
var scores = new List<Score>
{
    new Score { Name = "A", Value = 80 },
    new Score { Name = "B", Value = null },
    new Score { Name = "C", Value = 95 },
};

int? minScore = scores.Min(x => x.Value);

Console.WriteLine(minScore); // 80
C#

null は無視され、80 と 95 の最小値で 80 になります。
全件 null の場合は、結果も null です。

ここでの重要ポイントは、「int? の Min は戻り値も int? になる」ことです。


GroupBy と組み合わせて「○○ごとの最小値」を出す

顧客ごとの最小購入金額

public class Order
{
    public string Customer { get; set; } = "";
    public int Amount { get; set; }
}
C#
var orders = new List<Order>
{
    new Order { Customer = "A", Amount = 1000 },
    new Order { Customer = "A", Amount = 3000 },
    new Order { Customer = "B", Amount = 2000 },
};
C#
var minByCustomer = orders
    .GroupBy(o => o.Customer)
    .Select(g => new
    {
        Customer = g.Key,
        MinAmount = g.Min(x => x.Amount)
    });
C#

ここでの重要ポイントは、「g.Min(…) は“そのグループの中だけ”の最小値を計算する」ことです。

日付ごとの最小金額

public class SaleWithDate
{
    public DateTime Date { get; set; }
    public int Amount { get; set; }
}
C#
var dailyMin = salesWithDate
    .GroupBy(s => s.Date.Date)
    .Select(g => new
    {
        Date     = g.Key,
        MinAmount = g.Min(x => x.Amount)
    })
    .OrderBy(x => x.Date);
C#

「最小値を持つ要素そのもの」が欲しいとき

Min は“値”しか返さない

「最小値を持つレコードそのもの」が欲しい場合は、Min ではなく並び替えを使います。

OrderBy + FirstOrDefault で取る

var minSale = sales
    .OrderBy(s => s.Amount)
    .FirstOrDefault();
C#

金額の小さい順に並べ、先頭(=最小金額の明細)を取っています。

ここでの重要ポイントは、「Min は値、OrderBy + FirstOrDefault は要素そのもの」という使い分けです。


実務で意識してほしいこと

「何の最小値か」を変数名で明確にする

min だけだと意味が分かりません。

int minAmount = sales.Min(s => s.Amount);
DateTime earliest = logs.Min(x => x.OccurredAt);
C#

こうするだけで、コードの意図が一気に読みやすくなります。

最小値取得の前に「フィルタ → グルーピング」の順番を意識する

例えば「2026年2月の最小金額」を出したいなら、

int minFebAmount = sales
    .Where(s => s.Date.Year == 2026 && s.Date.Month == 2)
    .Min(s => s.Amount);
C#

「対象を絞ってから最小値を取る」という順番が大切です。


まとめ:「最小値取得ユーティリティ」は“いちばん小さい数字を素早く見つけるレーダー”

最小値取得の本質は、

「たくさんの明細の中から、“いちばん小さいもの”を素早く特定する」

ということです。

押さえておきたいポイントは、

  • 数値は Min()、オブジェクトは Min(x => x.プロパティ)
  • 空コレクションは例外になるので Any() でチェック
  • null 許容型の Min は null を無視し、戻り値も null
  • 「○○ごとの最小値」は GroupByg.Min(...)
  • 要素そのものが欲しいときは OrderBy + FirstOrDefault

このあたりが自然に書けるようになると、
“業務でよくある最小値取得”を LINQ でスマートに扱えるようになります。

タイトルとURLをコピーしました