LINQの主要メソッド一覧
概要
このページではLINQでよく使用されるメソッドの解説を行います。
LINQについての概要はLINQのページを参照してください。
LINQ拡張メソッドにはオーバーロードがたくさんあります。
全部を解説するのは無理なので、使用頻度が高めと思われる物を解説します。
このページを読むときの注意
特に説明がない限り、このページのサンプルコードでNumStr
というクラスが登場した場合、以下の自作クラスを使用します。
class NumStr
{
public int Num;
public string Str;
public NumStr(int n, string s)
{
Num = n;
Str = s;
}
}
同様に、PrintEnumerable
メソッドは、IEnumerableシーケンスの中身を表示します。
static void PrintEnumerable<T>(IEnumerable<T> e)
{
foreach(var x in e)
{
Console.Write("{0} ", x);
}
Console.WriteLine();
}
//要素がNumStrクラスの場合はこちらが呼び出される
static void PrintEnumerable(IEnumerable<NumStr> e)
{
foreach (var x in e)
{
Console.WriteLine("{0} {1}", x.Num, x.Str);
}
}
なお、サンプルコードなので各メソッドの戻り値を受け取る変数宣言ではデータ型を明示しています。
戻り値の型はメソッドによって変わりますし、いちいち指定するのも面倒なので実際のプログラムでは型推論(var)で受け取ったほうが良いでしょう。
要素の比較が必要なメソッドについて
LINQのいくつかのメソッドは内部的に要素同士を比較するものがあります。
例えば要素の並び替えをするOrderBy
メソッドは要素同士を比較して並べ替えの順序を決定しています。
これらのメソッドで比較の方法を自分で定義するには特定のインターフェイスの継承や比較のためのクラスの定義などが必要となります。
具体的な方法はオブジェクトの比較を参照してください。
メソッド一覧
シーケンスの生成
Enumerable.Repeat
Enumerable.Range
Enumerable.Repeat
メソッドは指定の要素を任意の数生成したシーケンスを返します。
Enumerable.Range
メソッドは指定の整数要素から任意の数連続させたシーケンスを返します。
//要素が「2」、要素数「5」のシーケンスを返す
IEnumerable<int> repeat = Enumerable.Repeat(2, 5);
//「2」から5つ分要素が連続するシーケンスを返す
IEnumerable<int> range = Enumerable.Range(2, 5);
PrintEnumerable(repeat);
PrintEnumerable(range);
2 2 2 2 2 2 3 4 5 6
Enumerable.Range
メソッドが生成するシーケンスの要素はint型で固定です。
変換
ToArray
ToList
ToDictionary
ToArray
メソッドはシーケンスを配列に変換します。
ToList
メソッドはシーケンスをListに変換します。
ToDictionary
メソッドはシーケンスをDictionaryに変換します。
List<NumStr> lstNS = new List<NumStr>()
{
new NumStr(1, "aaa"),
new NumStr(2, "bbb"),
new NumStr(3, "ccc")
};
NumStr[] toArray = lstNS.ToArray();
List<NumStr> toList = toArray.ToList();
//Numをキーにする
Dictionary<int, NumStr> toDictionary1 =
lstNS.ToDictionary(ns => ns.Num);
//↑は↓と同じ
//Dictionary<int, NumStr> _toDictionary1 = new Dictionary<int, NumStr>()
//{
// { 1, lstNS[0] },
// { 2, lstNS[1] },
// { 3, lstNS[2] }
//};
//Numをキーに、Strを値にする
Dictionary<int, string> toDictionary2 =
lstNS.ToDictionary(ns => ns.Num, ns => ns.Str);
//↑は↓と同じ
//Dictionary<int, string> _toDictionary2 = new Dictionary<int, string>()
//{
// { 1, "aaa" },
// { 2, "bbb" },
// { 3, "ccc" }
//};
ToDictionary
メソッドの第一引数はキーの指定です。
第二引数は指定すると値の指定です。
第二引数は省略可能です。
ToDictionary
メソッドの実行時に、キーとなる要素に重複がある場合は例外が発生します。
重複の可能性がある場合はあらかじめDistinct
などで重複要素を除去しておく必要があります。
IEqualityComparer
ToDictionary
メソッドのオーバーロードにはIEqualityComparer
型を引数に取るものがあります。
これはキーに指定する要素の比較のためのクラス(IEqualityComparer型を継承したクラス)を指定します。
例えば文字列の大文字小文字を無視する、参照型をキーにする、などの場合に使用されます。
詳細はIEqualityComparerを参照してください。
AsEnumerable
AsEnumerable
メソッドはシーケンスをIEnumerable型に変換します。
これはEnumerableクラスで定義されているメソッド(LINQ拡張メソッド)を明示的に呼び出したい場合に使用します。
例えばLINQにはReverse
というメソッドがありますが、ListクラスのインスタンスからそのままReverse
メソッドを呼び出すとListクラスのReverse
メソッドが呼ばれます。
AsEnumerable
でIEnumerableに変換してからReverse
メソッドを呼び出せばLINQのReverse
メソッドが呼ばれます。
List<int> nums = new List<int>()
{
11, 5, 9, 4, 6
};
IEnumerable<int> reverse = nums.AsEnumerable().Reverse();
//↓これだとListクラスのReverseメソッドが呼ばれてしまう
//IEnumerable<int> reverse = nums.Reverse();
ToLookup
ToLookup
メソッドは要素を指定のキーでグループ化します。
このメソッドはGroupBy
と良く似ており、使用方法もほぼ同じです。
ただしGroupBy
メソッドで可能な「データ型の変換」はToLookup
メソッドではできません。
グループ化というのは、キーが共通する要素同士を同じグループに分類して格納する、という意味です。
以下の例ではNumStr
クラスのフィールドNum
をキーとし、フィールドNum
が同じ要素同士をまとめたシーケンスを生成しています。
List<NumStr> lstNS = new List<NumStr>()
{
new NumStr(1, "aaa"),
new NumStr(2, "bbb"),
new NumStr(3, "ccc"),
new NumStr(1, "ddd"),
new NumStr(3, "eee"),
};
//Numをキーにして、共通のNumを持つ要素をまとめる
ILookup<int, NumStr> toLookup =
lstNS.ToLookup(ns => ns.Num);
foreach (IGrouping<int, NumStr> g in toLookup)
{
Console.Write(g.Key);
foreach (NumStr ns in g)
Console.Write(" {0}", ns.Str);
Console.WriteLine();
}
1 aaa ddd 2 bbb 3 ccc eee
戻り値はDictionaryクラスに似た構造のILookup<データ型1, データ型2>
というデータ型(インターフェイス)です。
Dictionaryクラスは「ひとつのキー(Key)に対してひとつの値(Value)」というものですが、ILookup型は「ひとつのキーに対して複数の値」といった感じの構造です。
この「複数の値」に、同じグループの要素がまとめて格納されています。
「ジャグ配列(配列の配列)」と考えたほうが分かりやすいかもしれません。
ILookup型とIGrouping型
ILookup型はIGrouping<データ型1, データ型2>
という型の集まりです。
Dictionaryクラスの要素がkeyVaulePair型の集合なのと同じようなものです。
IGrouping型もキーと値を持っていて、ひとつのキーに対して複数の値を保存しています。
キーはKey
プロパティで取得できますが、値は複数あるのでこれもまたforeach文で各要素を取り出すことができます。
なお、ILookup型はCount
プロパティを持っていて、要素数(つまりすべてのキーの数)を取得することができます。
またインデクサ(添え字)にキーを指定することで、そのキーが持つ要素の集合(IEnumerable型)を取得できます。
List<NumStr> lstNS = new List<NumStr>()
{
new NumStr(1, "aaa"),
new NumStr(2, "bbb"),
new NumStr(3, "ccc"),
new NumStr(1, "ddd"),
new NumStr(3, "eee"),
};
ILookup<int, NumStr> toLookup =
lstNS.ToLookup(ns => ns.Num);
//キーが"1"の要素すべてを取得し、Listに変換
List<NumStr> numStr_key1 = toLookup[1].ToList();
Console.WriteLine(lstNS.Count);
Console.WriteLine(toLookup.Count);
Console.WriteLine(numStr_key1.Count);
5 3 2
グループ化される要素の指定
第一引数のみを指定した場合は元のシーケンスの要素がグループ化されますが、第二引数でグループ化される要素を指定することもできます。
以下の例ではフィールドStr
がグループ化されます。
List<NumStr> lstNS = new List<NumStr>()
{
new NumStr(1, "aaa"),
new NumStr(2, "bbb"),
new NumStr(3, "ccc"),
new NumStr(1, "ddd"),
new NumStr(3, "eee"),
};
//型引数が<int, string>に変わっている
ILookup<int, string> toLookup =
lstNS.ToLookup(
ns => ns.Num,
ns => ns.Str);
foreach (IGrouping<int, string> g in toLookup)
{
Console.Write(g.Key);
foreach (string s in g)
Console.Write(" {0}", s);
Console.WriteLine();
}
1 aaa ddd 2 bbb 3 ccc eee
IEqualityComparer
ToLookup
メソッドのオーバーロードにはIEqualityComparer
型を引数に取るものがあります。
これはキーの比較の方法を指定したクラス(IEqualityComparerを継承したクラス)を指定します。
例えば文字列の大文字小文字を無視する、参照型をキーにする、などの場合に使用されます。
詳細はIEqualityComparerを参照してください。
Cast
Cast
メソッドは要素を指定のデータ型にキャストします。
キャストできない要素が含まれている場合は例外が発生します。
Cast
メソッドはよくある「int型→long型」のようなキャストはできません。
クラスのアップキャストやダウンキャスト用のメソッドです。
class Base { }
class Derived : Base { public int Num; }
//アップキャスト
List<Base> lstBase = new List<Base>()
{
new Derived { Num = 1 },
new Derived { Num = 2 },
new Derived { Num = 3 }
};
//ダウンキャスト
IEnumerable<Derived> cast = lstBase.Cast<Derived>();
OfType
OfType
メソッドは要素を指定のデータ型にキャストします。
キャストできない要素は結果から除外されます。
OfType
メソッドはよくある「int型→long型」のようなキャストはできません。
クラスのアップキャストやダウンキャスト用のメソッドです。
class Base { }
class DerivedA : Base { public int Num; }
class DerivedB : Base { public int Num; }
//アップキャスト
List<Base> lstBase = new List<Base>()
{
new DerivedA() { Num = 1 },
new DerivedB() { Num = 2 }, //違う型が混ざっている
new DerivedA() { Num = 3 }
};
//ダウンキャスト
//DerivedAに変換できない要素は除外される
IEnumerable<DerivedA> ofType = lstBase.OfType<DerivedA>();
foreach (DerivedA d in ofType)
{
Console.WriteLine(d.Num);
}
1 3
単一要素の取得
First
Last
ElementAt
Single
First
メソッドは最初の要素を返します。
Last
メソッドは最後の要素を返します。
ElementAt
メソッドは指定のインデックスの要素を返します。
Single
メソッドはシーケンスに要素がひとつだけ存在する場合に、その要素を返します。
これらのメソッドは該当する要素がない場合は例外が発生します。
Single
メソッドはシーケンス内に要素が二つ以上ある場合も例外が発生します。
List<int> nums = new List<int>()
{
1, 2, 5, 9, 11, 14, 18, 21
};
int first = nums.First();
int last = nums.Last();
int elementAt = nums.ElementAt(2);
List<int> nums2 = new List<int>()
{
9
};
int single = nums2.Single();
Console.WriteLine(first);
Console.WriteLine(last);
Console.WriteLine(elementAt);
Console.WriteLine(single);
1 21 5 9
条件の指定
First
、Last
、Single
メソッドは引数に条件を指定することもできます。
First
メソッドは先頭から数えて最初に条件に合致する要素を返します。
Last
メソッドは末尾から数えて最初に条件に合致する要素を返します。
Single
メソッドは条件に合致するひとつの要素を返します。
該当する要素がない場合は例外が発生します。
Single
メソッドは該当する要素が二つ以上ある場合も例外が発生します。
Single
メソッドはほとんどの場合でこちらの条件指定のオーバーロードが使用されるでしょう。
List<int> nums = new List<int>()
{
1, 2, 5, 9, 11, 14, 18, 21
};
int first = nums.First(x => x % 2 == 0); //2で割り切れる最初の要素
int last = nums.Last(x => x % 2 == 0); //2で割り切れる最後の要素
int single = nums.Single(x => x > 20); //20より大きいただ一つの要素
Console.WriteLine(first);
Console.WriteLine(last);
Console.WriteLine(single);
2 18 21
FirstOrDefault
LastOrDefault
ElementAtOrDefault
SingleOrDefault
これらは基本的にFirst
メソッドなどと同じですが、該当する要素が存在しない場合に既定値を返します。
(例外は発生しません)
SingleOrDefault
メソッドは該当する要素が二つ以上ある場合も既定値を返します。
ElementAtOrDefault
以外は引数に条件が指定できます。
List<int> nums = new List<int>()
{
//空
};
int first = nums.FirstOrDefault();
int last = nums.LastOrDefault();
int elementAt = nums.ElementAtOrDefault(2);
int single = nums.SingleOrDefault(x => x > 20);
Console.WriteLine(first);
Console.WriteLine(last);
Console.WriteLine(elementAt);
Console.WriteLine(single);
0 0 0 0
複数要素の取得
Where
Where
メソッドは条件に合う要素をすべて取得したシーケンスを返します。
要素を条件でフィルターすると言い換えることもできます。
List<int> nums = new List<int>()
{
1, 2, 5, 9, 11, 14, 18, 21
};
IEnumerable<int> where = nums.Where(n => n > 10);
PrintEnumerable(where);
11 14 18 21
要素番号の利用
以下のように条件判定メソッドの第二仮引数に変数(int型)を指定すると、現在操作中の要素のインデックス(要素番号)を条件判定メソッド内で使用できます。
List<int> nums = new List<int>()
{
1, 2, 5, 9, 11, 14, 18, 21
};
//要素番号が3以上、かつ奇数の要素を取得
IEnumerable<int> where = nums.Where(
(num, index) => index >= 3 && num % 2 != 0);
9 11 21
上記コードの引数index
には、シーケンスから値を取り出すたびにその先頭からの要素番号が格納されます。
条件判定メソッド内ではその要素番号を利用して、「先頭から3番目以降の要素」を条件に追加しています。
Take
Skip
Take
メソッドは先頭から指定した数の要素を返します。
Skip
メソッドは先頭から指定の数を省いた要素を返します。
List<int> nums = new List<int>()
{
1, 2, 5, 9, 11, 14, 18, 21
};
IEnumerable<int> take = nums.Take(3);
IEnumerable<int> skip = nums.Skip(3);
PrintEnumerable(take);
PrintEnumerable(skip);
1 2 5 9 11 14 18 21
指定の範囲の取得
Take
メソッドは引数にRange
構造体を指定することで、要素の始点と終点の範囲のシーケンスを取得することができます。
このオーバーロードは.NET6以降で使用できます。
List<int> nums = new List<int>()
{
1, 2, 5, 9, 11, 14, 18, 21
};
//2番目から5番目手前までのシーケンスを返す
IEnumerable<int> take = nums.Take(new Range(2, 5));
PrintEnumerable(take);
5 9 11
TakeWhile
SkipWhile
TakeWhile
メソッドは先頭から条件を満たす連続した要素を返します。
(条件を満たさなくなるまでの要素を返す)
SkipWhile
メソッドは先頭から条件を満たす連続した要素を省略し、残りを返します。
これらは条件を満たさなくなった要素以降に条件を満たす要素があっても無視されます。
引数を二つ取るメソッドを指定すると、現在操作中の要素のインデックス(要素番号)を条件判定メソッド内で使用できます。
(要素番号の利用を参照)
List<int> nums = new List<int>()
{
1, 2, 5, 9, 11, 14, 3, 4
};
//先頭から「n < 10」が成立しなくなるまでの要素
IEnumerable<int> takeWhile = nums.TakeWhile(n => n < 10);
//先頭から「n < 10」が成立しなくなった後の要素
IEnumerable<int> skipWhile = nums.SkipWhile(n => n < 10);
//要素のindexを使用する場合
//要素番号3未満、かつ要素の値が10未満
IEnumerable<int> takeWhile2 = nums.TakeWhile(
(num, index) => index < 3 && num < 10);
PrintEnumerable(takeWhile);
PrintEnumerable(skipWhile);
PrintEnumerable(takeWhile2);
1 2 5 9 11 14 3 4 1 2 5
DefaultIfEmpty
DefaultIfEmpty
メソッドはシーケンスが空の場合に既定値または任意の要素をひとつ持つシーケンスを返します。
シーケンスが空でない場合はそのシーケンスを返します。
List<int> nums1 = new List<int>()
{
1, 2, 3
};
List<int> nums2 = new List<int>();
IEnumerable<int> defaultIfEmpty1 = nums1.DefaultIfEmpty();
IEnumerable<int> defaultIfEmpty2 = nums2.DefaultIfEmpty();
IEnumerable<int> defaultIfEmpty3 = nums2.DefaultIfEmpty(-1);
PrintEnumerable(defaultIfEmpty1);
PrintEnumerable(defaultIfEmpty2);
PrintEnumerable(defaultIfEmpty3);
1 2 3 0 -1
シーケンスが空の場合、そのデータ型の既定値をひとつ持つシーケンスが返されます。
引数に任意の値を指定すると、既定値の代わりにその値を要素にひとつ持つシーケンスが返されます。
この値は自作クラスなどでも可能です。
Distinct
Distinct
メソッドは重複要素を省いたシーケンスを返します。
List<int> nums = new List<int>()
{
1, 2, 5, 2, 4, 5
};
IEnumerable<int> distinct = nums.Distinct();
PrintEnumerable(distinct);
1 2 5 4
IEqualityComparer
Distinct
メソッドのオーバーロードにはIEqualityComparer
型を引数に取るものがあります。
これはキーの比較の方法を指定したクラス(IEqualityComparerを継承したクラス)を指定します。
例えば文字列の大文字小文字を無視する、参照型をキーにする、などの場合に使用されます。
詳細はIEqualityComparerを参照してください。
ただし、比較クラスを用意するのは少し面倒なので、簡単な条件であればDistinct
は使用せずにGroupByとSelect
を組み合わせて実現する方法もあります。
List<NumStr> lstNS = new List<NumStr>()
{
new NumStr(1, "aaa"),
new NumStr(2, "bbb"),
new NumStr(1, "aaa")
};
IEnumerable<NumStr> distinct =
lstNS.GroupBy(ns => ns.Num)
.Select(g => g.First());
PrintEnumerable(distinct);
1 aaa 2 bbb
このコードは同じキーを持つ要素は重複要素と判定します。
指定のキーでグループ化した後、グループの先頭要素のみを取り出すことで重複を除去しています。
それぞれのメソッドの詳しい動作はGroupBy
とSelect
を参照してください。
データの集計
Count
Count
メソッドはシーケンスの要素数を返します。
引数に条件式を指定すると、条件に合致する要素数を返します。
List<int> nums = new List<int>()
{
8, 11, 5, 21, 7
};
int count1 = nums.Count();
int count2 = nums.Count(n => n < 10);
Console.WriteLine(count1);
Console.WriteLine(count2);
5 3
Min
Max
Min
メソッドはシーケンス中の最小値を返します。
Max
メソッドはシーケンス中の最大値を返します。
List<int> nums = new List<int>()
{
8, 11, 5, 21, 7
};
int min = nums.Min();
int max = nums.Max();
Console.WriteLine(min);
Console.WriteLine(max);
5 21
これらは比較対象のデータ(フィールド、プロパティ)を引数で指定することができます。
List<NumStr> lstNS = new List<NumStr>()
{
new NumStr(1, "aaa"),
new NumStr(2, "bbb"),
new NumStr(3, "ccc")
};
int min = lstNS.Min(ns => ns.Num);
int max = lstNS.Max(ns => ns.Num);
Console.WriteLine(min);
Console.WriteLine(max);
1 3
Sum
Average
Sum
メソッドはシーケンスの要素を合計した値を返します。
Average
メソッドはシーケンスの要素の平均を返します。
List<int> nums = new List<int>()
{
8, 11, 5, 21, 7
};
int sum = nums.Sum();
double average = nums.Average();
Console.WriteLine(sum);
Console.WriteLine(average);
52 10.4
これらは計算に使用するデータ(フィールド、プロパティ)を引数で指定することができます。
List<NumStr> lstNS = new List<NumStr>()
{
new NumStr(1, "aaa"),
new NumStr(2, "bbb"),
new NumStr(3, "ccc")
};
int sum = lstNS.Sum(ns => ns.Num);
double average = lstNS.Average(ns => ns.Num);
Console.WriteLine(sum);
Console.WriteLine(average);
6 2
Aggregate
Aggregate
メソッドはデータ集計の方法を自ら設計することができます。
Sum
やAverage
などと同じ計算もこのメソッドで行うことができます。
Aggregate
メソッドは使用方法がやや難解で、オーバーロードも含めて理解しなければ有効に使用することができません。
しかしかなり自由な処理が可能なので、使い方次第では便利なメソッドです。
引数ひとつ(func)
List<int> nums = new List<int>()
{
1, 2, 3, 4, 5
};
//Sumと同等の処理
int agregate = nums.Aggregate((x, y) => x + y);
Console.WriteLine(agregate);
15
Aggregate
メソッドは、メソッド(func)をひとつ与える形があります。
(デリゲート、ラムダ式)
このメソッドは引数をふたつ取り、以下のような動作となります。
nums
から要素を一つ取り出しx
に格納nums
から次の要素を取り出しy
に格納- 式を実行し、結果を
x
に格納 - 2~3をすべての要素分繰り返し、
x
を返す
x
とy
を実際の値に置き換えると、
- 1 + 2
- 3 + 3
- 6 + 4
- 10 + 5
となり、結果は「15」となります。
なお、この処理を行うメソッドはアキュムレータ関数と言います。
これは最終的な戻り値となる値をメソッド内に一時的に保存し、処理の度にその値を更新していく関数のことです。
引数ふたつ(seed, func)
アキュムレータ関数の前にseedと呼ばれる値を与えると、上記の「1」の手順の際にこの値がx
に格納されます。
seedはシーケンスの要素と同じ型である必要はなく、例えば以下のように数値型のシーケンスに対して文字列のseedを与えると、文字列結合を行うこともできます。
(string型 + int型の演算はstring型になるため)
List<int> nums = new List<int>()
{
1, 2, 3, 4, 5
};
string agregate = nums.Aggregate("", (x, y) => x + y);
Console.WriteLine(agregate);
12345
このコードの実行結果は数値(int型)ではなく「12345」という文字列であることに注意してください。
引数みっつ(seed, func, resultSelector)
seed、funcに続いて第三引数にresultSelectorというメソッドを与えることができます。
これはすべての要素に対するfuncの実行が終わった後に呼び出され、funcの結果の値を加工することができます。
List<int> nums = new List<int>()
{
1, 2, 3, 4, 5
};
//Averageと同等の処理
double agregate = nums.Aggregate(
0,
(x, y) => x + y,
x => x / (double)nums.Count);
Console.WriteLine(agregate);
3
resultSelectorの引数はひとつで、ここにfuncの実行結果が格納されます。
このサンプルではすべての要素を加算した後に要素数で割っているので、つまり平均値を求めています。
(キャストしているのはdouble型で演算を行うためです)
集合
Union
Except
Intersect
Union
メソッドは二つのシーケンスの和集合を返します。
これは両方のシーケンスの要素を、重複要素を省いてすべて合わせた新しいシーケンスを作ります。
Except
メソッドは二つのシーケンスの差集合を返します。
これは実行対象のシーケンスから、引数指定のシーケンス中に存在する要素を省いた新しいシーケンスを作ります。
Intersect
メソッドは二つのシーケンスの積集合を返します。
これは実行対象のシーケンスから、引数指定のシーケンス中に存在する要素を抽出した新しいシーケンスを作ります。
List<int> nums1 = new List<int>()
{
1, 2, 3, 4, 5
};
List<int> nums2 = new List<int>()
{
3, 4, 5, 6, 7
};
IEnumerable<int> union = nums1.Union(nums2);
IEnumerable<int> except = nums1.Except(nums2);
IEnumerable<int> intersect = nums1.Intersect(nums2);
PrintEnumerable(union);
PrintEnumerable(except);
PrintEnumerable(intersect);
1 2 3 4 5 6 7 1 2 3 4 5
IEqualityComparer
これらのメソッドはIEqualityComparer
型を引数に取るオーバーロードもあります。
これは要素の比較の方法を指定したクラス(IEqualityComparerを継承したクラス)を指定します。
例えば文字列の大文字小文字を無視する、参照型を比較する、などの場合に使用されます。
詳細はIEqualityComparerを参照してください。
結合
Concat
Concat
メソッドは二つのシーケンスを結合します。
List<int> nums1 = new List<int>()
{
1, 2, 3
};
List<int> nums2 = new List<int>()
{
2, 3, 4
};
IEnumerable<int> concat = nums1.Concat(nums2);
PrintEnumerable(concat);
1 2 3 2 3 4
Join
Join
メソッドは二つのシーケンスの共通するキーを用いて結合し、新しいシーケンスを作り出します。
//役職
class Position
{
public int ID;
public string Name;
public Position(int id, string name)
{
ID = id;
Name = name;
}
}
//人物
class Person
{
public int ID;
public string Name;
public int PositionID;
public Person(int id, string name, int positionID)
{
ID = id;
Name = name;
PositionID = positionID;
}
}
//社員
class Employee
{
public string Position;
public string Name;
public Employee(string position, string name)
{
Position = position;
Name = name;
}
}
static void Main(string[] args)
{
List<Position> positions = new List<Position>()
{
new Position(1, "部長"),
new Position(2, "課長"),
new Position(3, "係長"),
new Position(4, "ヒラ"),
};
List<Person> persons = new List<Person>()
{
new Person(1, "A山B太", 1),
new Person(2, "C谷D男", 2),
new Person(3, "E下F子", 2),
new Person(4, "G田H雄", 3),
new Person(5, "I森J美", 3),
new Person(6, "K川L子", 4),
new Person(7, "M沢N助", 4),
new Person(8, "O木P一", 4),
};
IEnumerable<Employee> employees = positions.Join(
persons,
position => position.ID,
person => person.PositionID,
(pos, per) => new Employee(pos.Name, per.Name));
foreach (Employee employee in employees)
{
Console.WriteLine("{0} {1}",
employee.Position,
employee.Name);
}
}
部長 A山B太 課長 C谷D男 課長 E下F子 係長 G田H雄 係長 I森J美 ヒラ K川L子 ヒラ M沢N助 ヒラ O木P一
このコードはPosition
クラスのフィールドID
と、Person
クラスのフィールドPositionID
を共通のキーとし、二つのクラス(のフィールド)同士を結合してEmployee
という新しいクラスを作り出しています。
Join
メソッドの第一引数には結合するシーケンスを指定します。
第二引数は結合されるシーケンスのキーを指定します。
第三引数は結合するシーケンス(第一引数のシーケンス)のキーを指定します。
第四引数はそれぞれのシーケンスから必要なフィールドを取り出して新しいデータ型を生成するためのメソッド(ラムダ式)を指定します。
今回はPerson
クラスのName
とPositon
クラスのName
を使ってEmployee
クラスを作り出しています。
新しいクラス型(今回はEmployee
クラス)をわざわざ用意する必要がなければ匿名型でも構いません。
IEqualityComparer
Join
メソッドにはIEqualityComparer
を引数に取るオーバーロードもがあります。
これはキーの比較の方法を指定したクラス(IEqualityComparerを継承したクラス)を指定します。
例えば文字列の大文字小文字を無視する、参照型をキーにする、などの場合に使用されます。
詳細はIEqualityComparerを参照してください。
GroupJoin
GroupJoin
メソッドは二つのシーケンスの共通するキーを用いて新しいシーケンスを作り出します。
新しいシーケンスはキー毎にグループ分けされます。
//役職
class Position
{
//Joinメソッド参照
}
//人物
class Person
{
//Joinメソッド参照
}
//指定の役職名を持つ人一覧
class PositionAndPersons
{
public string Position;
public List<Person> Persons;
public PositionAndPersons(string position, List<Person> persons)
{
this.Position = position;
this.Persons = persons;
}
}
static void Main(string[] args)
{
List<Position> positions = new List<Position>()
{
new Position(1, "部長"),
new Position(2, "課長"),
new Position(3, "係長"),
new Position(4, "ヒラ"),
};
List<Person> persons = new List<Person>()
{
new Person(1, "A山B太", 1),
new Person(2, "C谷D男", 2),
new Person(3, "E下F子", 2),
new Person(4, "G田H雄", 3),
new Person(5, "I森J美", 3),
new Person(6, "K川L子", 4),
new Person(7, "M沢N助", 4),
new Person(8, "O木P一", 4),
};
IEnumerable<PositionAndPersons> paps = positions.GroupJoin(
persons,
position => position.ID,
person => person.PositionID,
(pos, per) => new PositionAndPersons(pos.Name, per.ToList()));
foreach (PositionAndPersons pap in paps)
{
Console.Write(pap.Position);
foreach (Person person in pap.Persons)
{
Console.Write(" {0}", person.Name);
}
Console.WriteLine();
}
}
部長 A山B太 課長 C谷D男 E下F子 係長 G田H雄 I森J美 ヒラ K川L子 M沢N助 O木P一
Position
クラスとPerson
クラスはJoin
メソッドの解説と同じなのでそちらを参照してください。
Join
メソッドとの違いは、第四引数に指定するメソッドの第二引数(per
)が、共通するキーを持つ要素でグループ化(シーケンス化)されている点です。
IEqualityComparer
GroupJoin
メソッドにはIEqualityComparer
型を引数に取るオーバーロードもあります。
これはキーの比較の方法を指定したクラス(IEqualityComparerを継承したクラス)を指定します。
例えば文字列の大文字小文字を無視する、参照型をキーにする、などの場合に使用されます。
詳細はIEqualityComparerを参照してください。
Zip
Zip
メソッドは二つのシーケンスの要素同士を任意のルールで合体させます。
このメソッドは.NET Framework4以降で使用できます。
int[] nums = new int[]
{
1, 2, 3, 4, 5
};
List<string> strs = new List<string>()
{
"a", "bb", "ccc"
};
//単純な結合
IEnumerable<string> joins1 = nums.Zip(
strs,
(n, s) => n + s);
//数値を文字数で乗算
IEnumerable<int> joins2 = nums.Zip(
strs,
(n, s) => n * s.Length);
PrintEnumerable(joins1);
PrintEnumerable(joins2);
1a 2bb 3ccc 1 4 9
第一引数は結合する対象のシーケンスを指定します。
要素のデータ型に制限はありません。
第二引数は合体させるルールとなるメソッドを指定します。
引数はふたつで、第一引数が結合されるシーケンス(nums
)、第二引数が第一引数に指定したシーケンス(strs
)から取り出された各要素となります。
それぞれのシーケンスの同じ要素番号同士の要素を結合させるので、戻り値となるシーケンスの要素数は要素が少ない方のシーケンスの要素数となります。
タプルの生成
Zip
メソッドはタプルを生成することもできます。
引数ひとつ(要素がふたつのタプル)は.NET Core3.0以降で使用できます。
引数ふたつ(要素がみっつのタプル)は.NET6以降で使用できます。
(どちらも.NET Frameworkでは使用不可)
int[] nums = new int[]
{
1, 2, 3, 4, 5
};
List<string> strs = new List<string>()
{
"a", "bb", "ccc"
};
List<char> chars = new List<char>()
{
'x', 'y', 'z'
};
IEnumerable<(int, string)> tuple1 = nums.Zip(strs);
IEnumerable<(int, string, char)> tuple2 = nums.Zip(strs, chars);
PrintEnumerable(tuple1);
PrintEnumerable(tuple2);
(1, a) (2, bb) (3, ccc) (1, a, x) (2, bb, y) (3, ccc, z)
判定
Contains
All
Any
SequenceEqual
Contains
メソッドは特定の要素が含まれているかを判定します。
All
メソッドはすべての要素が条件を満たすかを判定します。
Any
メソッドはいずれかの要素が条件を満たすか(ひとつでも条件を満たすか)を判定します。
SequenceEqual
メソッドは別のシーケンスと要素が等しいかを判定します。
Any
メソッドは引数を指定しない場合、シーケンスに何らかの要素が存在する場合に真を返します。
Contains
メソッドはListにそのまま適用するとListクラス内で定義されているContains
メソッドが呼び出されるため、AsEnumerable
メソッドを利用して呼び出しています。
List<int> nums1 = new List<int>()
{
3, 5, 9, 12, 15
};
List<int> nums2 = new List<int>()
{
3, 5, 9, 12, 15
};
bool contains = nums1.AsEnumerable().Contains(12);
bool all = nums1.All(n => n < 10);
bool any1 = nums1.Any();
bool any2 = nums1.Any(n => n < 10);
bool sequenceEqual = nums1.SequenceEqual(nums2);
Console.WriteLine(contains);
Console.WriteLine(all);
Console.WriteLine(any1);
Console.WriteLine(any2);
Console.WriteLine(sequenceEqual);
True False True True True
IEqualityComparer
Contains
、SequenceEqual
メソッドはIEqualityComparer
型を引数に取るオーバーロードもあります。
これは要素の比較方法を指定したクラス(IEqualityComparerを継承したクラス)を指定します。
例えば文字列の大文字小文字を無視する、参照型を比較する、などの場合に使用されます。
詳細はIEqualityComparerを参照してください。
ソート(並べ替え)
OrderBy
OrderByDescending
Reverse
OrderBy
メソッドは要素を昇順で並べ替えたシーケンスを返します。
OrderByDescending
メソッドは要素を降順で並べ替えたシーケンスを返します。
Reverse
メソッドは要素の順序を前後反転させたシーケンスを返します。
List<int> nums = new List<int>()
{
3, 5, 2, 1, 4
};
IEnumerable<int> orderBy = nums.OrderBy(x => x);
IEnumerable<int> orderByDescending = nums.OrderByDescending(x => x);
IEnumerable<int> reverse = nums.AsEnumerable().Reverse();
PrintEnumerable(orderBy);
PrintEnumerable(orderByDescending);
PrintEnumerable(reverse);
1 2 3 4 5 5 4 3 2 1 4 1 2 5 3
Reverse
メソッドはListにそのまま適用するとListクラス内で定義されているReverse
メソッドが呼び出されるので、AsEnumerable
メソッドを利用して呼び出しています。
OrderBy
、OrderByDescending
メソッドの引数は要素の比較に指定する値の指定です。
int型などの単純な型の場合はそのまま要素を返すメソッドを指定します。
クラス型などの場合は比較の指定するフィールドを指定できます。
List<NumStr> lstNS = new List<NumStr>()
{
new NumStr(2, "bbb"),
new NumStr(1, "aaa"),
new NumStr(3, "ccc"),
};
IEnumerable<NumStr> orderBy = lstNS.OrderBy(ns => ns.Num);
PrintEnumerable(orderBy);
1 aaa 2 bbb 3 ccc
IComparer
OrderBy
、OrderByDescending
メソッドはIComparer
型を引数に取るオーバーロードもあります。
これは要素の比較方法を指定したクラス(IComparerを継承したクラス)を指定します。
例えば独自の比較方法を使用する、クラスのメンバーで比較する、などの場合に使用されます。
詳細はIComparerを参照してください。
ThenBy
ThenByDescending
ThenBy
メソッドはソートされたシーケンス内で同じ順位の要素を昇順で並べ替えます。
ThenByDescending
メソッドはソートされたシーケンス内で同じ順位の要素を降順で並べ替えます。
これらは要するに、並べ替えの順序が同じ要素があった場合に、次の並べ替え条件となる値を指定することができます。
List<NumStr> lstNS = new List<NumStr>()
{
new NumStr(5, "aaa"),
new NumStr(8, "bbb"),
new NumStr(2, "ccc"),
new NumStr(2, "ddd"),
new NumStr(8, "eee")
};
IEnumerable<NumStr> thenBy =
lstNS.OrderBy(ns => ns.Num)
.ThenBy(ns => ns.Str);
IEnumerable<NumStr> thenByDescending =
lstNS.OrderBy(ns => ns.Num)
.ThenByDescending(ns => ns.Str);
PrintEnumerable(thenBy);
Console.WriteLine();
PrintEnumerable(thenByDescending);
2 ccc 2 ddd 5 aaa 8 bbb 8 eee 2 ddd 2 ccc 5 aaa 8 eee 8 bbb
IComparer
ThenBy
、ThenByDescending
メソッドはIComparer
型を引数に取るオーバーロードもあります。
これは要素の比較方法を指定したクラス(IComparerを継承したクラス)を指定します。
例えば独自の比較方法を使用する、クラスのメンバーで比較する、などの場合に使用されます。
詳細はIComparerを参照してください。
射影
Select
Select
メソッドはそれぞれの要素に任意の式を適用したシーケンスを返します。
以下の例では、nums
の各要素を二倍したシーケンスを返します。
メソッドの引数を二つ指定すると、現在操作中の要素のインデックス(要素番号)をメソッド内で使用できます。
List<int> nums = new List<int>()
{
1, 2, 3, 4, 5
};
//各要素を二倍にした新しいシーケンス
IEnumerable<int> select1 = nums.Select(n => n * 2);
//要素のindexを使用する場合
//要素番号が3以上の要素を二倍にする
IEnumerable<int> select2 = nums.Select(
(n, index) => index >= 3 ? n * 2 : n);
PrintEnumerable(select1);
PrintEnumerable(select2);
2 4 6 8 10 1 2 3 8 10
戻り値の要素の型は自由です。
newでクラスのインスタンスのシーケンスを得ることもできますし、クラスから任意のメンバーのシーケンスを得ることもできます。
List<string> strs = new List<string>()
{
"aaa", "bbb", "ccc"
};
//「NumStr」型のシーケンスを得る
IEnumerable<NumStr> select1 = strs.Select(
(s, index) => new NumStr(index, s));
//「string」型のシーケンスを得る
IEnumerable<string> select2 =
select1.Select(ns => ns.Str);
PrintEnumerable(select1);
PrintEnumerable(select2);
0 aaa 1 bbb 2 ccc aaa bbb ccc
SelectMany
SelectMany
メソッドはシーケンスの要素がシーケンスであるとき、各要素に式を適用した上で平坦化して返します。
要するに、「配列の配列」のような構造のシーケンスに対し、各要素にメソッドを適用し、「ただの配列」にして返します。
データ構造が「配列の配列」から「配列」に変換され、これを平坦化というそうです。
(実際に返ってくるのはIEnumerable型です)
「配列の配列」の各要素にメソッドを適用することはSelect
でも可能ですが、結果がフラット(平坦)になる点が異なります。
List<List<int>> nums = new List<List<int>>()
{
new List<int>() { 1, 2, 3 },
new List<int>() { 4, 5, 6 },
new List<int>() { 7, 8, 9 }
};
IEnumerable<int> selectMany =
nums.SelectMany(x => x.Select(y => y * 2));
//Selectメソッドのみの場合
IEnumerable<IEnumerable<int>> select =
nums.Select(x => x.Select(y => y * 2));
//通常のforeachでOK
foreach (int x in selectMany)
{
Console.Write("{0} ", x);
}
Console.WriteLine();
//こちらは二重のforeachが必要
foreach (IEnumerable<int> x in select)
{
foreach (int y in x)
Console.Write("{0} ", y);
}
2 4 6 8 10 12 14 16 18 2 4 6 8 10 12 14 16 18
Select
メソッドと同様に、要素番号を取得して利用することもできます。
IEnumerable<int> selectMany =
nums.SelectMany(
(x, index) => x.Where(
n => index > 1 && n % 2 == 0));
他のシーケンスと連携
SelectMany
メソッドは基本的に対象のシーケンスの要素を使用しますが、別のシーケンスを利用することもできます。
class NumStrs
{
public int Num;
public List<string> Strs;
public NumStrs(int n, List<string> s)
{
Num = n;
Strs = s;
}
}
static void Main(string[] args)
{
List<NumStrs> lstNS = new List<NumStrs>()
{
new NumStrs(1, new List<string>() { "a", "bb" }),
new NumStrs(2, new List<string>() { "ccc", "dddd" }),
};
List<string> strs = new List<string>()
{
"a", "bb", "ccc"
};
IEnumerable<NumStr> selectMany1 = lstNS.SelectMany(
ns => ns.Strs,
(ns, s) => new NumStr(ns.Num, s));
//元のシーケンスに関係ないシーケンスも指定できる
IEnumerable<NumStr> selectMany2 = lstNS.SelectMany(
_ => strs,
(ns, s) => new NumStr(ns.Num, s));
PrintEnumerable(selectMany1);
Console.WriteLine();
PrintEnumerable(selectMany2);
}
1 a 1 bb 2 ccc 2 dddd 1 a 1 bb 1 ccc 2 a 2 bb 2 ccc
第一引数はメソッドで、引数にはシーケンスの各要素が格納されます。
戻り値はIEnamerable型(を継承するクラス)を指定します。
この戻り値が次の第二引数のメソッドで使用されます。
第二引数もメソッドです。
第一引数はシーケンスの各要素が格納されます。
第二引数は先ほどの第一引数のメソッドの戻り値の各要素が格納されます。
このメソッドの戻り値の集合(IEnumerable型)がSelectMany
メソッドの戻り値となります。
上のコードの最初の例(selectMany1
)では、NumStrs
クラスの内部に保存しているstring型リストを第二引数のメソッドで使用しています。
次の例(selectMany2
)では、クラスには関係ない別のローカル変数(strs
)を第二引数のメソッドで使用しています。
第一引数の戻り値に指定するシーケンスはIEnamerable型(を継承したクラス)であれば何でも使用できます。
これも要素番号を取得して利用することもできます。
IEnumerable<NumStr> selectMany = lstNS.SelectMany(
(ns, index) => ns.Strs.Where(s => index > 0),
(ns, s) => new NumStr(ns.Num, s));
GroupBy
GroupBy
メソッドは要素を指定のキーでグループ化します。
グループ化というのは指定のキーが共通する要素同士をまとめる(分類する)という意味です。
以下の例ではNumStr
クラスのフィールドNum
をキーとし、同じ値を持つインスタンスを同じグループとみなして分類しています。
戻り値は二次元配列のような構造で、foreach文のネスト(入れ子)で結果を表示します。
List<NumStr> lstNS = new List<NumStr>()
{
new NumStr(1, "aaa"),
new NumStr(2, "bbb"),
new NumStr(3, "ccc"),
new NumStr(1, "ddd"),
new NumStr(3, "eee"),
};
IEnumerable<IGrouping<int, NumStr>> groupBy =
lstNS.GroupBy(ns => ns.Num);
foreach (IGrouping<int, NumStr> g in groupBy)
{
foreach (NumStr ns in g)
Console.Write("{0}-{1} ", ns.Num, ns.Str);
Console.WriteLine();
}
1-aaa 1-ddd 2-bbb 3-ccc 3-eee
IGrouping型についてはILookup型とIGrouping型も参照してください。
グループ化される要素の指定
第二引数でグループ化対象の要素を指定することもできます。
以下の例ではフィールドStr
をグループ化される要素にしています。
List<NumStr> lstNS = new List<NumStr>()
{
new NumStr(1, "aaa"),
new NumStr(2, "bbb"),
new NumStr(3, "ccc"),
new NumStr(1, "ddd"),
new NumStr(3, "eee"),
};
IEnumerable<IGrouping<int, string>> groupBy =
lstNS.GroupBy(ns => ns.Num, ns => ns.Str);
foreach (IGrouping<int, string> g in groupBy)
{
Console.Write("{0} ", g.Key);
foreach (string s in g)
Console.Write("{0} ", s);
Console.WriteLine();
}
1 aaa ddd 2 bbb 3 ccc eee
戻り値の要素の型のがNumStr
からstring型に変わっていることに注目してください。
戻り値のデータ型の変換
上記のどちらの場合も、シーケンスに格納される値はIGrouping<データ型1, データ型2>
型(インターフェイス)です。
(→ILookup型とIGrouping型を参照)
これを一般的な型(Listとか)に変換したい場合は、第二引数に指定するメソッドの引数を二つ指定します。
List<NumStr> lstNS = new List<NumStr>()
{
new NumStr(1, "aaa"),
new NumStr(2, "bbb"),
new NumStr(3, "ccc"),
new NumStr(1, "ddd"),
new NumStr(3, "eee"),
};
IEnumerable<List<NumStr>> groupBy = lstNS.GroupBy(
ns => ns.Num,
(key, group) => group.ToList());
foreach (List<NumStr> nss in groupBy)
{
foreach (NumStr ns in nss)
Console.Write("{0}-{1} ", ns.Num, ns.Str);
Console.WriteLine();
}
1-aaa 1-ddd 2-bbb 3-ccc 3-eee
このようにすると、第一引数key
にはキーに指定した値が、第二引数group
にはkey
でグループ化されたシーケンス(IEnumerable型)が渡されます。
このメソッドで返す値によって、最終的に返されるシーケンスの型が変わります。
(IEnumerable<任意のデータ型>
にできる)
そのままgroup
を返すだけでも構いませんが、今回はToList
メソッドでList化しています。
最終的な戻り値はIEnumerable<List<NumStr>>
型となります。
グループ化される要素の指定+データ型の変換
グループ化対象の要素を指定しつつデータ型を変換する場合は、第一引数にキーを指定、第二引数にグループ化対象のデータを指定、第三引数に変換したいデータ型を返すメソッドを指定、という形式で行います。
List<NumStr> lstNS = new List<NumStr>()
{
new NumStr(1, "aaa"),
new NumStr(2, "bbb"),
new NumStr(3, "ccc"),
new NumStr(1, "ddd"),
new NumStr(3, "eee"),
};
//匿名型なのでvar
//IEnumerable<匿名型<int, IEnumerable<string>>>
var groupBy = lstNS.GroupBy(
x => x.Num, //キーの指定
x => x.Str, //グループ化の対象指定
(Key, Group) => new { Key, Group }); //戻り値の指定
foreach (var x in groupBy)
{
Console.Write("{0} ", x.Key);
foreach (string s in x.Group)
Console.Write("{0} ", s);
Console.WriteLine();
}
1 aaa ddd 2 bbb 3 ccc eee
IEqualityComparer
GroupByメソッドのオーバーロードにはIEqualityComparer型を引数に取るものがあります。
これはキーの比較の方法を指定したクラス(IEqualityComparerを継承したクラス)を指定します。
例えば文字列の大文字小文字を無視する、参照型をキーにする、などの場合に使用されます。
詳細はIEqualityComparerを参照してください。