プロジェクションとは、オブジェクトを新しい形式に変換する操作を指します。これは、多くの場合、後で使用されるプロパティのみで構成されます。 プロジェクションを使用すると、各オブジェクトから構築された新しい型を構築できます。 プロパティを投影し、それに対して数学関数を実行できます。 元のオブジェクトを変更せずに投影することもできます。
Von Bedeutung
これらのサンプルでは、System.Collections.Generic.IEnumerable<T> データ ソースを使用します。
System.Linq.IQueryProvider に基づくデータ ソースでは、System.Linq.IQueryable<T> データ ソースと式ツリーが使用されます。 式ツリーには、許可される C# 構文に制限があります。 さらに、IQueryProvider
などの各 データ ソースでは、より多くの制限が課される場合があります。 ご利用のデータ ソースのドキュメントをご覧ください。
プロジェクションを実行する標準のクエリ演算子メソッドを次のセクションに示します。
メソッド
メソッド名 | 説明 | C# クエリ式の構文 | 詳細情報 |
---|---|---|---|
選択する | 変換関数に基づく値をプロジェクト化します。 | select |
Enumerable.Select Queryable.Select |
SelectMany | 変換関数に基づく値のシーケンスをプロジェクトし、それらを 1 つのシーケンスにフラット化します。 | 複数の from 句を使用する |
Enumerable.SelectMany Queryable.SelectMany |
ZIPファイル | 指定した 2 から 3 個のシーケンスの要素を持つタプルのシーケンスを生成します。 | 適用されません。 | Enumerable.Zip Queryable.Zip |
Select
次の例では、 select
句を使用して、各文字列の最初の文字を文字列の一覧に投影します。
List<string> words = ["an", "apple", "a", "day"];
var query = from word in words
select word.Substring(0, 1);
foreach (string s in query)
{
Console.WriteLine(s);
}
/* This code produces the following output:
a
a
a
d
*/
次のコードでは、メソッド構文を使用した同等のクエリを示しています。
List<string> words = ["an", "apple", "a", "day"];
var query = words.Select(word => word.Substring(0, 1));
foreach (string s in query)
{
Console.WriteLine(s);
}
/* This code produces the following output:
a
a
a
d
*/
SelectMany
次の例では、複数の from
句を使用して、各文字列の各単語を文字列の一覧に投影します。
List<string> phrases = ["an apple a day", "the quick brown fox"];
var query = from phrase in phrases
from word in phrase.Split(' ')
select word;
foreach (string s in query)
{
Console.WriteLine(s);
}
/* This code produces the following output:
an
apple
a
day
the
quick
brown
fox
*/
次のコードでは、メソッド構文を使用した同等のクエリを示しています。
List<string> phrases = ["an apple a day", "the quick brown fox"];
var query = phrases.SelectMany(phrase => phrase.Split(' '));
foreach (string s in query)
{
Console.WriteLine(s);
}
/* This code produces the following output:
an
apple
a
day
the
quick
brown
fox
*/
SelectMany
メソッドは、最初のシーケンス内のすべての項目と 2 番目のシーケンスのすべての項目を照合する組み合わせを形成することもできます。
var query = from number in numbers
from letter in letters
select (number, letter);
foreach (var item in query)
{
Console.WriteLine(item);
}
次のコードでは、メソッド構文を使用した同等のクエリを示しています。
var method = numbers
.SelectMany(number => letters,
(number, letter) => (number, letter));
foreach (var item in method)
{
Console.WriteLine(item);
}
Zip
Zip
プロジェクション演算子にはいくつかのオーバーロードがあります。 すべての Zip
メソッドは、2 つ以上の異種型のシーケンスで動作します。 最初の 2 つのオーバーロードは、指定されたシーケンスの対応する位置型を持つタプルを返します。
次のコレクションについて考えてみましょう。
// An int array with 7 elements.
IEnumerable<int> numbers = [1, 2, 3, 4, 5, 6, 7];
// A char array with 6 elements.
IEnumerable<char> letters = ['A', 'B', 'C', 'D', 'E', 'F'];
これらのシーケンスを一緒に投影するには、 Enumerable.Zip<TFirst,TSecond>(IEnumerable<TFirst>, IEnumerable<TSecond>) 演算子を使用します。
foreach ((int number, char letter) in numbers.Zip(letters))
{
Console.WriteLine($"Number: {number} zipped with letter: '{letter}'");
}
// This code produces the following output:
// Number: 1 zipped with letter: 'A'
// Number: 2 zipped with letter: 'B'
// Number: 3 zipped with letter: 'C'
// Number: 4 zipped with letter: 'D'
// Number: 5 zipped with letter: 'E'
// Number: 6 zipped with letter: 'F'
Von Bedeutung
zip 操作の結果のシーケンスの長さが、最短のシーケンスよりも長くなることはありません。
numbers
コレクションとletters
コレクションの長さが異なるため、numbers
コレクションの最後の要素は、それと組み合わせる要素がないため、結果のシーケンスから省略されます。
2 番目のオーバーロードは、 third
シーケンスを受け入れます。 別のコレクション (つまり、 emoji
) を作成してみましょう。
// A string array with 8 elements.
IEnumerable<string> emoji = [ "🤓", "🔥", "🎉", "👀", "⭐", "💜", "✔", "💯"];
これらのシーケンスを一緒に投影するには、 Enumerable.Zip<TFirst,TSecond,TThird>(IEnumerable<TFirst>, IEnumerable<TSecond>, IEnumerable<TThird>) 演算子を使用します。
foreach ((int number, char letter, string em) in numbers.Zip(letters, emoji))
{
Console.WriteLine(
$"Number: {number} is zipped with letter: '{letter}' and emoji: {em}");
}
// This code produces the following output:
// Number: 1 is zipped with letter: 'A' and emoji: 🤓
// Number: 2 is zipped with letter: 'B' and emoji: 🔥
// Number: 3 is zipped with letter: 'C' and emoji: 🎉
// Number: 4 is zipped with letter: 'D' and emoji: 👀
// Number: 5 is zipped with letter: 'E' and emoji: ⭐
// Number: 6 is zipped with letter: 'F' and emoji: 💜
前のオーバーロードと同様に、 Zip
メソッドはタプルを投影しますが、今回は 3 つの要素を含みます。
3 番目のオーバーロードは、結果セレクターとして機能する Func<TFirst, TSecond, TResult>
引数を受け取ります。 圧縮されているシーケンスから新しい結果のシーケンスを投影できます。
foreach (string result in
numbers.Zip(letters, (number, letter) => $"{number} = {letter} ({(int)letter})"))
{
Console.WriteLine(result);
}
// This code produces the following output:
// 1 = A (65)
// 2 = B (66)
// 3 = C (67)
// 4 = D (68)
// 5 = E (69)
// 6 = F (70)
上記の Zip
オーバーロードでは、指定された関数が number
および letter
対応する要素に適用され、 string
結果のシーケンスが生成されます。
Select
対 SelectMany
Select
とSelectMany
の両方の作業は、ソース値から結果値 (または値) を生成することです。
Select
では、ソース値ごとに 1 つの結果値が生成されます。 したがって、全体的な結果は、ソース コレクションと同じ数の要素を持つコレクションになります。 これに対し、 SelectMany
では、各ソース値から連結されたサブコレクションを含む 1 つの全体的な結果が生成されます。
SelectMany
の引数として渡される変換関数は、ソース値ごとに列挙可能な値のシーケンスを返す必要があります。
SelectMany
は、これらの列挙可能なシーケンスを連結して、1 つの大きなシーケンスを作成します。
次の 2 つの図は、これら 2 つのメソッドのアクションの概念上の違いを示しています。 いずれの場合も、セレクター (変換) 関数が各ソース値から花の配列を選択すると仮定します。
次の図は、ソース コレクションと同じ数の要素を持つコレクションを Select
が返す方法を示しています。
この図は、 SelectMany
配列の中間シーケンスを連結して、各中間配列の各値を含む最終的な結果値を 1 つにまとめる方法を示しています。
コード例
次の例では、 Select
と SelectMany
の動作を比較します。 このコードでは、ソース コレクション内の花の名前の各リストから項目を取得して、花の "ブーケ" を作成します。 次の例では、変換関数 Select<TSource,TResult>(IEnumerable<TSource>, Func<TSource,TResult>) 使用する "単一値" は値のコレクションです。 この例では、各サブシーケンス内の各文字列を列挙するために、追加の foreach
ループが必要です。
class Bouquet
{
public required List<string> Flowers { get; init; }
}
static void SelectVsSelectMany()
{
List<Bouquet> bouquets =
[
new Bouquet { Flowers = ["sunflower", "daisy", "daffodil", "larkspur"] },
new Bouquet { Flowers = ["tulip", "rose", "orchid"] },
new Bouquet { Flowers = ["gladiolis", "lily", "snapdragon", "aster", "protea"] },
new Bouquet { Flowers = ["larkspur", "lilac", "iris", "dahlia"] }
];
IEnumerable<List<string>> query1 = bouquets.Select(bq => bq.Flowers);
IEnumerable<string> query2 = bouquets.SelectMany(bq => bq.Flowers);
Console.WriteLine("Results by using Select():");
// Note the extra foreach loop here.
foreach (IEnumerable<string> collection in query1)
{
foreach (string item in collection)
{
Console.WriteLine(item);
}
}
Console.WriteLine("\nResults by using SelectMany():");
foreach (string item in query2)
{
Console.WriteLine(item);
}
}
こちらも参照ください
.NET