C#
 Computer >> コンピューター >  >> プログラミング >> C#

【C#】LINQのSelectとSelectManyの違いをサンプルコードでわかりやすく解説


はじめに

C#のLINQにおけるSelectSelectManyは、どちらもシーケンス(コレクション)の各要素を変換する「プロジェクション演算子」に分類されます。しかし、両者は返す結果の構造が大きく異なり、この違いを正しく理解することがLINQを使いこなすうえで非常に重要です。

Select演算子の特徴

Selectは、ソースシーケンスの各要素に対して1つの結果値を生成する演算子です。入力の要素数と出力の要素数は必ず一致し、オブジェクトのコレクションから特定のプロパティを抽出したり、値を変換したりする場面でよく使われます。

SelectMany演算子の特徴

SelectManyもプロジェクション演算子の一種ですが、各要素をIEnumerable<T>に射影し、その結果得られる複数のシーケンスを1つのシーケンスへ平坦化(フラット化)する点が最大の特徴です。「コレクションの中にコレクションがある」といった入れ子構造のデータを扱う際に威力を発揮します。

サンプルコードで確認する

ここでは、「Cap」「Shoes」という商品カテゴリを表すDemoクラスを用意し、それぞれがブランド名のリスト(Contents)を持つ例を見てみましょう。SelectとSelectManyの両方でContentsを取得し、動作の違いを比較します。

class Demo{
public string Name { get; set; }
public List<string> Contents { get; set; }

public static List<Demo> GetAllContents(){
List<Demo> listContents = new List<Demo>{
new Demo{
Name = "Cap",
Contents = new List<string> { "Nike", "Adidas" }
},
new Demo{
Name = "Shoes",
Contents = new List<string> { "Nike", "Puma", "Adidas" }
},
};
return listContents;
}
}

class Program{
static void Main(){
// Select:各要素ごとにList<string>がそのまま返される
IEnumerable<List<string>> result = Demo.GetAllContents().Select(s => s.Contents);
foreach (List<string> stringList in result){
foreach (string str in stringList){
Console.WriteLine(str);
}
}

Console.WriteLine("---Select Many---");

// SelectMany:複数のリストが1つのシーケンスに平坦化される
IEnumerable<string> resultSelectMany = Demo.GetAllContents().SelectMany(s => s.Contents);
foreach (string str in resultSelectMany){
Console.WriteLine(str);
}
Console.ReadKey();
}
}

実行結果

Nike
Adidas
Nike
Puma
Adidas
---Select Many---
Nike
Adidas
Nike
Puma
Adidas

結果の読み方:どこが違うのか

コンソールへの最終出力だけを見ると、両者はまったく同じに見えます。しかし、途中のデータ構造には決定的な違いがあります。

Selectを使用した場合、resultの型はIEnumerable<List<string>>となり、「リストの中にさらにリストが入った」入れ子構造になります。そのため、中身の文字列を表示するにはforeachを2重に入れ子にする必要があります。

一方、SelectManyを使用した場合、複数のリストはあらかじめ1つのIEnumerable<string>へ平坦化されているため、1重のforeachだけで全要素を処理できます。コードが簡潔になり、可読性も向上するのがメリットです。

使い分けのまとめ

項目SelectSelectMany
戻り値の構造変換後の要素をそのまま列挙(入れ子は維持される)複数のシーケンスを1つに平坦化
出力の要素数入力と同じ要素数各要素が持つ子要素の合計数
主な用途プロパティの抽出・値の変換入れ子になったコレクションの展開

「各要素から1つの値を取り出したいならSelect、入れ子になったコレクションを展開して1つの流れにしたいならSelectMany」と覚えておくと、状況に応じた適切な使い分けができるようになります。

  1. DirectX11とDirectX12の違いを徹底解説!ゲーマーが知っておくべきポイント

    DirectXは、ソフトウェアがマルチメディアコンテンツを描画し、グラフィックハードウェアと通信するために使用されるAPI(アプリケーションプログラミングインターフェース)のコレクションです。DirectXの主要な構成要素であるDirect3Dは、ソフトウェアとグラフィックスハードウェア間の通信を担っています。ハードウェアは製品ごとに異なるため、標準化された関数や呼び出しのライブラリを通じてグラフィックカードと通信することで、ゲーム開発を大幅にスピードアップできます。これは「ハードウェア抽象化」と呼ばれ、APIの最も重要な役割となっています。車に例えてみましょう。1台の車の運転ができれば、ほぼ

  2. C#の配列とリスト(List)の違いとは?特徴と基本的な使い方を解説

    C#における配列(Array)とリスト(List<T>)は、どちらも複数のデータをまとめて扱うためのコレクションですが、その性質には重要な違いがあります。 配列とリストの主な違い 配列は、生成時にサイズが固定される、同じ型の要素を順番に格納するコレクションです。一度作成した後は要素数を変更できません。 一方、Listはジェネリックコレクションであり、要素の追加や削除に応じてサイズが自動的に拡張・縮小します。この柔軟性が最大の特徴です。 項目配列List<T> サイズ固定可変(動的) 要素の追加不可(再作成が必要)Addメソッドで可能 宣言方法型[] 変数名Lis