LINQの基礎

LINQを使用すると、配列的なデータ(シーケンス)の様々な処理が見通し良く効率的にできる。 「シーケンス」というのは配列やリストを抽象化した概念であるが、 LINQの対象は「シーケンス」なので、配列やリストなどの違いを意識せずに使用できる。

メソッド名 機能 返すデータ型
Sum 和の計算 数値
Average 平均の計算 数値
Max 最大値 数値
Min 最小値 数値
Count データの個数を取得 数値
ToList シーケンスをリストに変換 リスト
ToArray シーケンスを配列に変換 配列
ElementAt 指定の要素を取り出す 要素のデータ型
Where 条件を満たすデータを抽出 シーケンス
Select データの加工 シーケンス

Sum

基本的な使用法.

int[] a = { 1, 2, 3 };
Console.WriteLine(a.Sum());
実行結果. 6

Sum() がLINQのメソッドである。通常、和の計算は for や foreach を使用するが、 上のコードでは、そのような処理が「a.Sum()」というたった 7 文字で完了している。 このように簡潔なコードが書けることが LINQ を使用するメリットになる。

ラムダ式を用いた利用法.

int[] a = { 3, 1, 4 };
Console.WriteLine(a.Sum(x => 2 * x));
実行結果. 16

1変数関数を引数に与える利用法もある。上のコードでは、要素を2倍して足し合わせている。 つまり、6+2+8=16 を計算している。

Count

基本的な使用法.

int[] a = { 3, 1, 4, 1, 5, 9 };
Console.WriteLine(a.Count());
実行結果. 6

配列aの要素数は6個。この値を取得することは Length を用いても可能であるが、Count() はより高度。 指定した条件を満たす要素をカウントすることができる。

ラムダ式を用いた利用法.

int[] a = { 3, 1, 4, 1, 5, 9, 2 };
Console.WriteLine(a.Count((x) => x % 2 == 0));
実行結果. 2

解説. 条件を満たす要素の個数をカウントしている。条件というのは ラムダ式で表現された部分

(x) => x % 2 == 0

であるがこれは x が 2 で割って余りが 0 、つまり、 x が偶数のときにtrue、 奇数の時falseになる 関数になっており結果として、偶数要素の個数をカウントすることになる。 「3,1,4,1,5,9,2」の偶数の要素は「4,2」なので 2 が結果として得られる。

Where

条件を満たす要素を取得する際に用いる。注意すべきは、元のデータが配列であってもリストであっても Whereを施すとシーケンスに変化してしまう点である。
次は偶数のデータを取り出し表示するプログラムになる。

int[] a = { 3, 1, 4, 1, 5, 9, 2 };
foreach (var item in a.Where(x => x % 2 == 0)){
    Console.WriteLine(item);
}
実行結果. 
4
2

もし、配列として抽出したデータを受け取りたい場合は次のようにシーケンスを配列にする ToArray() が必要になる。

int[] a = { 3, 1, 4, 1, 5, 9, 2 };
int[] b = a.Where(x => x % 2 == 0).ToArray();
foreach (var item in b){
    Console.WriteLine(item);
}
実行結果.
4
2

解説. このコードでは、aから偶数要素を取り出し、その結果を配列bに代入している。 この際、b は配列だから、直接 a.Where(x => x % 2 == 0) を代入できない。というのも aは配列であるが、a.Where() は「シーケンス」になるためである。 ただし、シーケンスから配列への変換は簡単で、単に ToArray() を施せばOKである。

Select

データを加工するのが Select である。数値データであれば、各要素を 2 倍する。 文字列データであれば、先頭に番号を付加するなどの使用が考えられる。
Whereと同じ注意が必要である。つまり、元のデータが配列やリストであってもSelectを適用すると シーケンスに変化する点である。しかもSelectの場合どんなデータ型のシーケンスになるかは状況による。

Select は1変数関数 f を用いると シーケンス{a1,a2,a3,...,} から シーケンス{f(a1),f(a2),f(a3),...}を得るメソッドになる。 したがって、関数fの戻り値によって、どんなデータ型のシーケンスになるかが変わる。

int[] a = { 3, 1, 4 };
foreach (var item in a.Select(x=>2*x)){
    Console.WriteLine(item);
}
実行結果.
6
2
8

Selectした結果をリストとして受けとるときは ToList() を用いる。

int[] a = { 3, 1, 4 };
List b = a.Select(x => 2*x).ToList();

次のプログラムは、int型の配列{1,2,3}からstring型の配列{"x1","x2","x3"}を得るものである。

int[] a = { 1, 2, 3 };
string[] words = a.Select(n => "x" + n).ToArray();
foreach (var item in words)
{
    Console.WriteLine(item);
}
実行結果.
x1
x2
x3

今回は扱わないが、2変数関数を引数に持つ利用法もあり、これを利用すると配列のインデックスが使用できる。