目次
LINQ to Object
(IEnumerable
を実装するコレクションクラスに対して実行)。
拡張メソッドとして(見た目)使用する場合は標準クエリ演算子と呼ぶらしい。
便利。
クエリ式
標準クエリ演算子の一部はクエリ式で記述することができる。
from ~ in
で 要素を取り出し、select
で返す内容を選択する
in
の後に続くオブジェクトは IEnumerable(<T>)
, IQueryable<T>
を実装しているクラス
var data = new[]
{
new { id = 1, name = "One" },
new { id = 2, name = "Two" },
new { id = 3, name = "Three" },
};
// 条件なしで name を取り出す
var names = from datum in data select datum.name;
foreach (string name in names)
{
Debug.Write(name); // OneTwoThree
}
上のコードは下のコードと等価
var data = new[]
{
new { id = 1, name = "One" },
new { id = 2, name = "Two" },
new { id = 3, name = "Three" },
};
var names2 = data.Select(datum => datum.name);
foreach (string name in names2)
{
Debug.Write(name); // OneTwoThree
}
where
で絞り込む
var data = new[]
{
new { id = 1, name = "One" },
new { id = 2, name = "Two" },
new { id = 3, name = "Three" },
};
var names = from datum in data
where datum.id == 2 // 条件指定 : イコール記号は1つではない
select datum.name;
foreach (string name in names)
{
Debug.WriteLine(name); // Two
}
// 上のコードと等価
names = data.Where(datum => datum.id == 2)
.Select(datum => datum.name);
foreach (string name in names)
{
Debug.WriteLine(name); // Two
}
int[] nums = new int[] { 1, 3, 5, 7, 8 };
// 標準クエリ演算子ではselectを省略できる
var results = nums.Where(num => num <= 5);
foreach (int num in results)
{
Debug.WriteLine(num); // 1, 3, 5 が出力される
}
// クエリ式ではコンパイルエラーとなる
results = from num in results where num <= 5; // コンパイルエラー
orderby
で並べ替え
descending
で降順に。
string[] strings = new string[]{
"B",
"D",
"A",
"C",
};
var results = from str in strings
where str[0] < 'D'
orderby str[0] descending
select str;
foreach(string str in results)
{
Debug.WriteLine(str);
// C, B, A の順に出力される
}
var results = new List<int> { 3, 4, 1, 2 }
.OrderByDescending(number => number)
.Select(number => number * 100);
foreach (int number in results)
{
Debug.WriteLine(number);
// 400, 300, 200, 100 の順に出力される
}
orderby r.a, r.b descending, r.c
のように複数指定できる。
標準クエリ演算子を使用する場合は
.OrderBy(r=>r.a).ThenByDescending(r=>r.b).ThenBy(r=>r.c)
となる。
from ~ in
のネストと SelectMany
拡張メソッド
from ~ in
を連続して記述することで、最初の from ~ in
で選択したデータに対して、さらに from ~ in
でデータを選択することができる。
SelectMany
拡張メソッドは from ~ in
を2回使用した場合と同等。
var table = new[]
{
new { array = new int[]{ 1, 2, 3}},
new { array = new int[]{ 4, 5}},
new { array = new int[]{ 6}},
};
var results = from record in table // tableからそれぞれの要素を取り出し
from number in record.array // 取り出した要素から、その要素が持つ値を取り出す
select number;
// ↓ "123456" と出力される
results.ToList().ForEach(number => Debug.Write(number));
Debug.WriteLine(string.Empty);
var selectMany = table.SelectMany(record => record.array);
// ↓ "123456" と出力される
selectMany.ToList().ForEach(number => Debug.Write(number));
//
// SelectManyで要素のインデックスを利用する方法
//
var table = new[]
{
new { array = new int[]{ 1, 2, 3}},
new { array = new int[]{ 4, 5}},
new { array = new int[]{ 6}},
};
table.SelectMany((record, index) =>
{
Debug.Write(index + "," + record.array.Length + "|");
return record.array;
}).ToList(); // "0,3|1,2|2,1|" と出力される
//
// SelectManyのもうちょい複雑なもの
// これのインデックス付きのものも有り
//
var result = table.SelectMany(
record => record.array.Reverse(),
(record, number) => number);
// ↓ "321546" と出力される
result.ToList().ForEach(number => Debug.Write(number));
//
// from~in + where の繰り返し
//
var table = new[]
{
new { id=1, array = new int[]{ 1, 2, 3}},
new { id=2, array = new int[]{ 4, 5}},
new { id=3, array = new int[]{ 6}},
};
var result = from record in table
where record.id != 2
from number in record.array
where number % 2 == 0
select number * 10;
// ↓ "2060" が出力される
result.ToList().ForEach(number=>Debug.WriteLine(number));
group ~ by
でグループ化
var table = new[]{
new {id = "A", number = 1},
new {id = "A", number = 2},
new {id = "B", number = 1},
new {id = "B", number = 10},
new {id = "B", number = 100},
};
var groups = from record in table
group record.number by record.id;
foreach (System.Linq.IGrouping<string, int> group in groups)
{
Debug.Write("ID=" + group.Key + " Numbers=");
foreach (int number in group)
{
Debug.Write(number + " ");
}
Debug.WriteLine("");
}
// 以下の通りに出力される
// ID=A Numbers=1 2
// ID=B Numbers=1 10 100
上と下は等価
var table = new[]{
new {id = "A", number = 1},
new {id = "A", number = 2},
new {id = "B", number = 1},
new {id = "B", number = 10},
new {id = "B", number = 100},
};
var groups = table.GroupBy(
record => record.id, // Key Selector
record => record.number); // Element Selector
foreach (System.Linq.IGrouping<string, int> group in groups)
{
Debug.Write("ID=" + group.Key + " Numbers=");
foreach (int number in group)
{
Debug.Write(number + " ");
}
Debug.WriteLine("");
}
// 以下の通りに出力される
// ID=A Numbers=1 2
// ID=B Numbers=1 10 100
join ~ on ~ equal
で結合
指定したキーで結合したものの組み合わせを全て抽出できる。
equals
で結合できるものが無い部分は抽出されない抽出されない。
var tableA = new[]
{
new { id=1, array = new int[]{ 1, 2, 3}},
new { id=3, array = new int[]{ 4, 5}},
new { id=5, array = new int[]{ 6}},
};
var tableB = new[]
{
new { id=1, name = "hoge1" },
new { id=1, name = "hoge2" },
new { id=2, name = "piyo1" },
new { id=2, name = "piyo2" },
new { id=3, name = "foobar1" },
new { id=3, name = "foobar2" },
};
var results = from a in tableA
join b in tableB
on a.id equals b.id
select new { id = a.id, array = a.array, name = b.name };
foreach (var result in results)
{
Debug.WriteLine("id={0}, array.Length={1}, name={2}",
result.id,
result.array.Length,
result.name);
// 出力は以下の通り
// id=1, array.Length=3, name=hoge1
// id=1, array.Length=3, name=hoge2
// id=3, array.Length=2, name=foobar1
// id=3, array.Length=2, name=foobar2
}
上と下は等価
var tableA = new[]
{
new { id=1, array = new int[]{ 1, 2, 3}},
new { id=3, array = new int[]{ 4, 5}},
new { id=5, array = new int[]{ 6}},
};
var tableB = new[]
{
new { id=1, name = "hoge1" },
new { id=1, name = "hoge2" },
new { id=2, name = "piyo1" },
new { id=2, name = "piyo2" },
new { id=3, name = "foobar1" },
new { id=3, name = "foobar2" },
};
var results = tableA.Join(tableB,
a => a.id,
b => b.id,
(a, b) => new { id = a.id, array = a.array, name = b.name });
foreach (var result in results)
{
Debug.WriteLine("id={0}, array.Length={1}, name={2}",
result.id,
result.array.Length,
result.name);
// 出力は以下の通り
// id=1, array.Length=3, name=hoge1
// id=1, array.Length=3, name=hoge2
// id=3, array.Length=2, name=foobar1
// id=3, array.Length=2, name=foobar2
}
join ~ on ~ equal ~ into
でグループ結合(GroupJoin
)
指定したキーで結合したものの組み合わせを1つのグループとして抽出。
Join
と違って GroupJoin
では結合元となる要素は全て抽出される
var tableA = new[]
{
new { id=1, array = new int[]{ 1, 2, 3}},
new { id=3, array = new int[]{ 4, 5}},
new { id=5, array = new int[]{ 6}},
};
var tableB = new[]
{
new { id=1, name = "hoge1" },
new { id=1, name = "hoge2" },
new { id=2, name = "piyo1" },
new { id=2, name = "piyo2" },
new { id=3, name = "foobar1" },
new { id=3, name = "foobar2" },
};
var results = from a in tableA
join b in tableB
on a.id equals b.id
into c
select new { id = a.id, array = a.array, c = c };
foreach (var result in results)
{
var dump = new StringBuilder();
result.c.ToList().ForEach(c=>dump.Append(c));
Debug.WriteLine("id={0}, array.Length={1}, c ={2}",
result.id,
result.array.Length,
dump.ToString());
// 出力は以下の通り
// id=1, array.Length=3, c ={ id = 1, name = hoge1 }{ id = 1, name = hoge2 }
// id=3, array.Length=2, c ={ id = 3, name = foobar1 }{ id = 3, name = foobar2 }
// id=5, array.Length=1, c =
}
上と下は等価
var tableA = new[]
{
new { id=1, array = new int[]{ 1, 2, 3}},
new { id=3, array = new int[]{ 4, 5}},
new { id=5, array = new int[]{ 6}},
};
var tableB = new[]
{
new { id=1, name = "hoge1" },
new { id=1, name = "hoge2" },
new { id=2, name = "piyo1" },
new { id=2, name = "piyo2" },
new { id=3, name = "foobar1" },
new { id=3, name = "foobar2" },
};
var results = tableA.GroupJoin(
tableB, // Inner : 結合したいIEnumerable
a => a.id, // outerKeySelector
b => b.id, // innerKeySelector
(a, c) => new { id = a.id, array = a.array, c = c });// resultSelector
foreach (var result in results)
{
var dump = new StringBuilder();
result.c.ToList().ForEach(c=>dump.Append(c));
Debug.WriteLine("id={0}, array.Length={1}, c ={2}",
result.id,
result.array.Length,
dump.ToString());
// 出力は以下の通り
// id=1, array.Length=3, c ={ id = 1, name = hoge1 }{ id = 1, name = hoge2 }
// id=3, array.Length=2, c ={ id = 3, name = foobar1 }{ id = 3, name = foobar2 }
// id=5, array.Length=1, c =
}
select ~ into
select
の結果に対して更にクエリ式を繋げたい場合は into
で繋げる
var numbers = new double[] { 0.4, 0.8, 1.2, 1.6 };
var results =
from a in numbers
select a * 10
into b
where b < 10
select b * 10;
results.ToList().ForEach(num => Debug.WriteLine(num));
// 40
// 80
// と出力される
var numbers = new double[] { 1 };
var results =
from a in numbers
select a + 10
into b
select b + 100
into c
select c + 1000
into d
select d + 10000;
// 11111 と出力される
results.ToList().ForEach(num => Debug.WriteLine(num));
from
での型指定と Cast
from
で取り出す時に型を指定できる。Cast
標準クエリ演算子と等価。
ただし指定した型が異なると InvalidCastException
が投げられる。
var arr = new object[] { 1, "hoge" };
var result = from int element in arr
select element;
Debug.WriteLine(result.First().GetType()); // System.Int32
result.Last(); // InvalidCastException
var arr = new int[] { 1 };
var result = arr.Cast<long>();
result.First(); // InvalidCastException
let
let
を利用するとクエリ式中で変数を使える。
int[] array = new int[] { 1 };
var results = from integer in array
let x10 = integer * 10
let x20 = integer * 20
select new { a = integer, b = x10, c = x20 };
var r = results.First();
Debug.WriteLine(r.a); // "1" と出力される
Debug.WriteLine(r.b); // "10" と出力される
Debug.WriteLine(r.c); // "20" と出力される