double av = (new List<int>{12,123,5,6546,8,9,1}).Average();
あらゆる型の項目に対して動作するオーバーロード版
ラムダを引数に取る
var ttlAge = persons.Sum(person => person.Age);
var avAge = persons.Average(person => person.Age);
var mxAge = persons.Max(person => person.Age);
var mnAge = persons.Min(person => person.Age);
Aggregate
すべてを調べて1つの値を得る演算子を一般化
合計、最大、平均を書き換える
var s2 = persons.Aggregate(0.0, (ttl, person) => ttl + person.Age);
var m2 = persons.Aggregate(0.0, (max, person) => max > person.Age ? max : person.Age);
var a2 = persons.Aggregate(new { ttl=0, cnt=0 }, (p1, person) => new { ttl = p1.ttl + person.Age,cnt = p1.cnt + 1 }, p2 => p2.ttl / p2.cnt);
累積器
reduce と呼ばれる機能のLINQにおける名称
foldと呼ばれる場合もある
集合演算子
3つの演算子
2つのソースをまとめる
一般的な集合演算
数学の集合とは異なる
Distinct
重複を取り除く
より集合に近い状態とする
Intersect
両方に含まれる
Except
一方にあるのにもう片方に含まれない
Union
どちらか、もしくは両方に含まれる
シーケンス全体を扱ったり、順序を維持するための演算子
Reverse
要素の順序を反転
Concat
2つのシーケンスを1つにまとめる
順序は維持される
Zip
2つのシーケンスを1つにまとめる
要素をペアに処理する
string[] nums = { "1", "2", "3", "4", "5" };
string[] alphas = {"a", "b", "c", "d", "e" };
var zipped = nums.Zip(alphas, (num, alpha) => num + ":" + alpha);
foreach( string s in zipped)
{
Console.WriteLine(s);
}
ソースの長さが異なる場合には、短い方の最後に到達で終了
SequenceEqual
2つのソースの項目数、値がすべて同じ場合、true
グループ化
group句
IGrouping<TKey,TItem> を実装する項目のコレクションを生成する
var persons = new List<Person> {
new Person() { Team="A",Name="hoge" },
new Person() { Team="B",Name="foo" },
new Person() { Team="A",Name="bar" },
new Person() { Team="C",Name="fuga"},
};
var teamGroups = from person in persons
group person by person.Team;
foreach(var group in teamGroups)
{
Console.WriteLine(group.Key);
foreach(var person in group)
{
Console.WriteLine(person.Name);
}
}
項目の射影を使ったグループ式の展開
クエリ式はselectかgroupが最後の句となる必要がある
group句がある場合に、それが最後である必要はない
into キーワードによりクエリの残りで反復処理するための範囲変数が作られる
orderbyやwhereなどほかの句でも利用できる
規定の結果IGrouping<TKey,TItem>も変更できる
var teams = from person in persons
group person by person.Team into team
select $"Team:{team.Key}:{team.Count()}";
グループ射影を使ったグループ化クエリの展開
var teams = persons.GroupBy(person => person.Team)
.Select(team => $"Team{team.Key}:{team.Count()}");
同じ意味を直接的に表すオーバーロド
2つのラムダをとる
1つめはグループ化条件
2つめはグループオブジェクトを生成するため
最初の引数にKeyが渡る
var teams = persons.GroupBy(person => person.Team,
(teamName,team) => $"Team{teamName}:{team.Count()}");
キー、項目とグループの射影を使ったGroupBy演算子
3つのラムダをとる
どの項目によってグループ化するか
個々の項目がグループの中でどのようにあらわされるか
各オブジェクトを生成
var teams = persons.GroupBy(
person => person.Team,
person => person.Name,
(teamName,names) => $"Team{teamName}:{names.Count()}");
複合グループキー
複数のキーでグループ化したい場合、単純にキーに両方の値を入れる
var persons = new List<Person> {
new Person() { Team="A",Name="hoge1",Lank="1" },
new Person() { Team="A",Name="hoge2",Lank="1" },
new Person() { Team="A",Name="hoge3",Lank="1" },
new Person() { Team="B",Name="foo",Lank="2" },
new Person() { Team="A",Name="bar",Lank="2" },
new Person() { Team="C",Name="fuga",Lank="3"},
};
var teamLankGroup = from person in persons
group person by new { person.Team, person.Lank };
foreach (var teamLank in teamLankGroup)
{
Console.WriteLine($"Team={teamLank.Key.Team},Lank={teamLank.Key.Lank},Count={teamLank.Count()}");
}
Join
異なるソースから関連性のあるデータをクエリから使用できるようにする
var teams = new List<Team> {
new Team() {Code="A",Name="TeamA" },
new Team() {Code="B",Name="TeamB" },
new Team() {Code="C",Name="TeamC" }
};
var persons = new List<Person> {
new Person() { Team="A",Name="hoge1"},
new Person() { Team="B",Name="foo"},
new Person() { Team="C",Name="fuga"}
};
var pt = from person in persons
join team in teams
on person.Team equals team.Code
select new { PersonName=person.Name, TeamName=team.Name};
foreach(var p in pt)
{
Console.WriteLine($"{p.PersonName},{p.TeamName}");
}
複合キーで結合する
var teams = new List<Team> {
new Team() {Code="A",SubCode="1",Name="TeamASub1" },
new Team() {Code="B",SubCode="1",Name="TeamBSub1" },
new Team() {Code="C",SubCode="1",Name="TeamCSub1" }
};
var persons = new List<Person> {
new Person() { Team="A",TeamSub="1",Name="hoge1"},
new Person() { Team="B",TeamSub="1",Name="foo"},
new Person() { Team="C",TeamSub="1",Name="fuga"}
};
var pt = from person in persons
join team in teams
on new { person.Team, person.TeamSub } equals new { Team=team.Code, TeamSub=team.SubCode }
select new { PersonName=person.Name, TeamName=team.Name};
foreach(var p in pt)
{
Console.WriteLine($"{p.PersonName},{p.TeamName}");
}
結合用の複合キーに匿名型を利用している
プロパティを合わせる
同一な構造を持つ同じアセンブリ内の匿名型は同じ型を共有する
コンパイラは匿名型に対して各メンバすべてを比較するEqualsメソッドを生成する
変換
シーケンスの型変換
var sourceSeq = sequence.Cast<Cource>();
Cast<T>
変換不能な場合、例外をスローする
OfType<T>
Cast<T> とほとんど同じだが、間違った型が存在した場合、例外をスローせずフィルタする
AsEnumerable<T>
何ら変更なくソースを返す
他のLINQプロバイダーによって処理される可能性があるものを扱う場合でも、確実にLINQ to Objectsによって処理されるようにする
ここから先はクライアント側で処理をすると明示
AsQueryable<T>
AsEnumerable<T>の反対に、LINQ to Objectsの代わりに確実に自分で作成したクエリを使用したいようなシナリオ
var persons = new List<Person> {
new Person() { Team="A",Name="hoge1"},
new Person() { Team="A",Name="hoge2"},
new Person() { Team="B",Name="foo"},
new Person() { Team="C",Name="fuga"}
};
var ta = persons.ToLookup(person => person.Team);
foreach (var p in ta["A"])
{
Console.WriteLine($"{p.Team},{p.Name}");
}
シーケンスの生成
Enumerable.Range
2つのintをとり、2つ目の引数まで1づつ大きくなるIEnumerable<int>を返す
Enumerable.Repeat<T>
型Tと回数を引数にとり、指定した回数生成する
Enumerable.Empty<T>
要素のないIEnumerable<T>が返される
他のLINQの実装
Entity Framework
LINQ to Entities
.NET Frameworkの一部
データベースとオブジェクト層を対応づけ
複数のデータベースベンダ製品がサポート
IQueryable<T> を使用
IEnemerable<T> から派生しているのでLINQ to Objectsの演算子を利用できる