bucket-sort logo bucket-sort

プログラミングとインフラエンジニアリングの覚え書き

  • Posts
  • About
  • Contact
  1. Home
  2. All Posts
  3. [C#] LINQ to Objects を理解する: ページング・集合演算・集計

[C#] LINQ to Objects を理解する: ページング・集合演算・集計

Jun 21, 2026 C# , .NET bucket-sort

前回は、LINQ to Objects のフィルター、射影、並べ替えを見ました。

今回は、ページング、件数取得、集合演算、重複除去、集計処理を整理します。

これらは、画面表示、検索結果、レポート作成などでよく使う操作です。

サンプルデータ

この記事では、次の商品データを使います。

class Product
{
    public string Name { get; }
    public string Category { get; }
    public int Price { get; }
    public bool InStock { get; }

    public Product(string name, string category, int price, bool inStock)
    {
        Name = name;
        Category = category;
        Price = price;
        InStock = inStock;
    }
}
List<Product> products = new List<Product>
{
    new Product("ノート", "文房具", 200, true),
    new Product("ペン", "文房具", 150, false),
    new Product("キーボード", "PC", 4500, true),
    new Product("マウス", "PC", 2500, true),
    new Product("モニター", "PC", 24000, false),
    new Product("USB メモリ", "PC", 1800, true)
};

Take で先頭から取り出す

Take は、先頭から指定した件数だけ取り出します。

var firstThree = products.Take(3);
foreach (Product product in firstThree)
{
    Console.WriteLine(product.Name);
}

実行結果です。

ノート
ペン
キーボード

検索結果の上位 10 件だけ表示したい場合などに使えます。

var top10 = products
    .OrderBy(product => product.Price)
    .Take(10);

Skip で先頭を飛ばす

Skip は、先頭から指定した件数を飛ばします。

var afterTwo = products.Skip(2);
foreach (Product product in afterTwo)
{
    Console.WriteLine(product.Name);
}

実行結果です。

キーボード
マウス
モニター
USB メモリ

Skip と Take を組み合わせると、ページングを実装できます。

int page = 2;
int pageSize = 2;

var pageItems = products
    .OrderBy(product => product.Name)
    .Skip((page - 1) * pageSize)
    .Take(pageSize);

この例では、2 ページ目の 2 件を取得しています。

範囲を使ったページング

.NET 6 以降では、Take に範囲を渡す書き方も使えます。

var pageItems = products
    .OrderBy(product => product.Name)
    .Take(2..4);

これは、0 から数えて 2 番目以上、4 番目未満の要素を取り出すイメージです。

ただし、ページ番号とページサイズから計算する処理では、従来の Skip と Take の方が意図が分かりやすいことも多いです。

var pageItems = products
    .Skip((page - 1) * pageSize)
    .Take(pageSize);

チームで読みやすい方を選ぶとよいでしょう。

Chunk で一定サイズごとに分割する

.NET 6 以降では、Chunk を使ってコレクションを一定サイズごとに分割できます。

var chunks = products.Chunk(2);
int pageNumber = 1;

foreach (Product[] chunk in chunks)
{
    Console.WriteLine($"ページ {pageNumber}");

    foreach (Product product in chunk)
    {
        Console.WriteLine(product.Name);
    }

    pageNumber++;
}

実行結果です。

ページ 1
ノート
ペン
ページ 2
キーボード
マウス
ページ 3
モニター
USB メモリ

Chunk は、一覧をまとめて分割して処理したいときに便利です。

Count で件数を取得する

Count は、要素数を取得します。

int count = products.Count();

条件付きで数えることもできます。

int inStockCount = products.Count(product => product.InStock);
Console.WriteLine($"在庫あり: {inStockCount}件");

実行結果です。

在庫あり: 4件

条件に合う要素が存在するかだけを知りたい場合は、Count() > 0 より Any() が適しています。

bool hasExpensiveProduct = products.Any(product => product.Price >= 10000);

Any は、条件に合う要素を見つけた時点で処理を終えられます。

TryGetNonEnumeratedCount

.NET 6 以降では、TryGetNonEnumeratedCount を使うと、列挙せずに件数を取得できる場合があります。

IEnumerable<Product> query = products.Where(product => product.InStock);

if (query.TryGetNonEnumeratedCount(out int count))
{
    Console.WriteLine($"列挙せずに件数を取得できました: {count}");
}
else
{
    Console.WriteLine("列挙しないと件数を取得できません");
}

配列や List<T> のように件数をすでに持っているコレクションでは、列挙せずに件数を取得できることがあります。

一方、Where のような条件付きクエリでは、実際に調べないと件数が分からない場合があります。

普段から頻繁に使うメソッドではありませんが、IEnumerable<T> の件数取得には「列挙が必要な場合がある」という点を理解しておくとよいです。

Distinct で重複を取り除く

Distinct は、重複を取り除きます。

string[] categories = { "PC", "文房具", "PC", "書籍", "文房具" };

var distinctCategories = categories.Distinct();
foreach (string category in distinctCategories)
{
    Console.WriteLine(category);
}

実行結果です。

PC
文房具
書籍

オブジェクトから特定のキーで重複を取り除きたい場合は、DistinctBy が使えます。

var categoryRepresentatives = products.DistinctBy(product => product.Category);

DistinctBy は .NET 6 以降で使えます。

集合演算

LINQ には、集合を扱うメソッドがあります。

string[] onlineUsers = { "Alice", "Bob", "Carol" };
string[] paidUsers = { "Bob", "Dave", "Alice" };

和集合は Union です。

var allUsers = onlineUsers.Union(paidUsers);

実行結果です。

Alice
Bob
Carol
Dave

積集合は Intersect です。

var onlineAndPaidUsers = onlineUsers.Intersect(paidUsers);

実行結果です。

Alice
Bob

差集合は Except です。

var onlineButNotPaidUsers = onlineUsers.Except(paidUsers);

実行結果です。

Carol

集合演算は、ベン図で考えると分かりやすいです。

メソッド 意味
Union どちらかに含まれる
Intersect 両方に含まれる
Except 左側にだけ含まれる
Distinct 重複を取り除く

Sum で合計する

Sum は数値の合計を求めます。

int totalPrice = products.Sum(product => product.Price);
Console.WriteLine($"合計: {totalPrice}円");

実行結果です。

合計: 33150円

条件と組み合わせることもできます。

int inStockTotal = products
    .Where(product => product.InStock)
    .Sum(product => product.Price);

Average、Min、Max

平均は Average です。

double averagePrice = products.Average(product => product.Price);

最小値は Min、最大値は Max です。

int minPrice = products.Min(product => product.Price);
int maxPrice = products.Max(product => product.Price);
Console.WriteLine($"平均: {averagePrice:F0}円");
Console.WriteLine($"最小: {minPrice}円");
Console.WriteLine($"最大: {maxPrice}円");

MinBy や MaxBy を使うと、最小値や最大値を持つ要素そのものを取得できます。

Product cheapest = products.MinBy(product => product.Price)!;
Product mostExpensive = products.MaxBy(product => product.Price)!;

MinBy / MaxBy は .NET 6 以降で使えます。

Aggregate で任意の集計を行う

Aggregate は、要素を順番に処理して 1 つの値にまとめるメソッドです。

たとえば、商品名を > でつないで表示してみます。

string route = products
    .Select(product => product.Name)
    .Aggregate((current, next) => $"{current} > {next}");
Console.WriteLine(route);

実行結果です。

ノート > ペン > キーボード > マウス > モニター > USB メモリ

合計や平均のような代表的な集計には専用メソッドがあります。

Aggregate は、専用メソッドでは表しにくい独自の集計が必要なときに使います。

まとめ

LINQ to Objects には、データの一部を取り出したり、集合として扱ったり、集計したりするためのメソッドが用意されています。

  • Take は先頭から指定件数を取り出す
  • Skip は先頭から指定件数を飛ばす
  • Skip と Take を組み合わせるとページングを実装できる
  • Chunk は一定サイズごとに分割する
  • Count は件数を数える
  • 存在確認だけなら Any が向いている
  • Distinct は重複を取り除く
  • Union、Intersect、Except は集合演算を行う
  • Sum、Average、Min、Max は代表的な集計処理を行う
  • Aggregate は任意の集計を行う

次回は、LINQ クエリが内部的にどのように表現されるのかを見ていきます。

C# .NET LINQ LINQ to Objects Skip Take Distinct 集計
← [C#] LINQ to Objects を理解する: フィルター・射影・並べ替え [C#] LINQ to Objects を理解する: クエリの内部表現 →

Related Posts

  • [C#] LINQ to Objects を理解する: クエリの内部表現 Jun 22, 2026
  • [C#] LINQ to Objects を理解する: フィルター・射影・並べ替え Jun 20, 2026
  • [C#] LINQ to Objects を理解する: 基本構文と遅延実行 Jun 19, 2026
  • [C#] 匿名型(Anonymous Types)の使い方 Jun 8, 2026

Table of Contents

  • サンプルデータ
  • Take で先頭から取り出す
  • Skip で先頭を飛ばす
  • 範囲を使ったページング
  • Chunk で一定サイズごとに分割する
  • Count で件数を取得する
  • TryGetNonEnumeratedCount
  • Distinct で重複を取り除く
  • 集合演算
  • Sum で合計する
  • Average、Min、Max
  • Aggregate で任意の集計を行う
  • まとめ

Recent Posts

  • [C#] プラグインで拡張できるアプリケーションを設計する Jul 9, 2026
  • [C#] 属性でコードに意味を与える Jul 8, 2026
  • [C#] 外部アセンブリを読み込み実行時に型を呼び出す Jul 7, 2026
  • [C#] リフレクションで型の中身を調べる Jul 6, 2026
  • [C#] 型メタデータとリフレクションの基礎を理解する Jul 5, 2026

Categories

  • C#120
  • .NET119
  • AWS27
  • Laravel16
  • Linux15
  • MySQL9
  • Apache8
  • PHP8
  • DynamoDB6
  • セキュリティ6
  • Nginx5
  • WordPress4
  • インフラ4
  • Hugo3
  • .NET Framework1
  • Aurora1
  • Diagnostics1
  • Filament1
  • Git1
  • SQS1

Tags

  • C#
  • .NET
  • AWS
  • Laravel
  • コレクション
  • PHP
  • セキュリティ
  • MySQL
  • Linux
  • パフォーマンス
  • Apache
  • System.Collections.Generic
  • デリゲート
  • Code Snippet
  • DynamoDB
  • LINQ
  • NoSQL
  • PHP-FPM
  • RDS
  • System.Collections
  • Windows
  • メモリ管理
  • リフレクション
  • DoS
  • Nginx
  • WordPress
  • メタデータ
  • ラムダ式
  • 監視
  • 設計
  • Amazon Linux 2023
  • Delegate
  • Docker
  • IDisposable
  • Ipset
  • Iptables
  • LINQ to Objects
  • OPCache
  • Pointer
  • Reflection
  • System.Collections.Specialized
  • Unsafe
  • Webサーバー
  • インターフェース
  • オブジェクト指向
  • クラス設計
  • デザインパターン
  • パターンマッチング
  • ポインター
  • 継承
Powered by Hugo & Explore Theme.