前回のLINQ to Objects 備忘録1に続いて備忘録を掲載します。今回はgroup by, join, distinct, union, intersect, except,count,sum,max,min,avarage,aggregateを扱います。データはLINQ to Objects 備忘録1で作成したデータを使用します。
確認環境は.NET 3.5, 開発環境はVisual Studio 2008 Professionalです。
今回取り扱っているクエリ式,拡張メソッド
クエリ式,拡張メソッド | 簡単な説明 |
group, GroupBy拡張メソッド | 集合の要素に対して、SQLのGroup By句に該当する処理を行います。 |
join,Join拡張メソッド | 集合の要素に対して、SQLのINNER JOINに該当する処理を行います。 |
group into, GroupJoin拡張メソッド | 集合の要素に対して、SQLのLEFT OUTER JOINに該当する処理を行います。 |
Distinct,Union,Intersect, Except拡張メソッド |
集合に対して、集合演算(和、積、差)を実施します。 |
Count,LongCount,Sum,Min,Max, Average,Aggregate拡張メソッド |
集合に対して、集約演算を行います。 |
1.LINQクエリメモ
1.1 Group演算子
SQLのgroup byにあたるLINQの演算子はgroup by です。対応する拡張メソッドはGroupBy()です。group by演算の結果は演算結果はIGrouping<TKey, TElement>インタフェースを実装したクラスのインスタンスのリストになります。サンプルを掲載します。
var categories = InitializeData(); var query = from c in categories group c by c.Name; foreach (var g in query) { Console.WriteLine("Key:" + g.Key + "-----------"); foreach (var item in g) { Console.Write(item.ToString()); } } var query1 = categories.GroupBy(c => c.Name, (key, elements) => new { Key = key, Count = elements.Count() }); foreach (var g in query1) { Console.WriteLine("Key:{0}, Count:{1}", g.Key, g.Count); }
実行結果は次のようになります。
Key:CS-----------
Category:ID=1, Name=CS
Article:ID=1, Title=CSArticle1, ReleaseDate=2004/01/01, ArticleType=Code
Article:ID=2, Title=CSArticle2, ReleaseDate=2004/02/01, ArticleType=Memo
Category:ID=4, Name=CS
Article:ID=6, Title=SqlArticle1, ReleaseDate=2008/10/03, ArticleType=Memo
Key:ASP-----------
Category:ID=2, Name=ASP
Article:ID=3, Title=CSArticle1, ReleaseDate=2004/01/01, ArticleType=Code
Article:ID=4, Title=AspArticle1, ReleaseDate=2002/12/21, ArticleType=Memo
Key:SQL-----------
Category:ID=3, Name=SQL
Article:ID=5, Title=SqlArticle1, ReleaseDate=2006/02/03, ArticleType=Code
Key:CS, Count:2
Key:ASP, Count:1
Key:SQL, Count:1
1.2 Join演算子
SQLのinnjer join等価結合にあたるLINQのクエリ式です。対応する拡張メソッドはJoin()です。joinを行う場合は結合条件に左にfromで指定したオブジェクト,右にjoinで指定したリストを記載します。
var anotations = new[] { new { CategoryID = 1, Anotation= "CSharp Programming" }, new {CategoryID = 2, Anotation = "ASP.NET Programming"} }; var categories = InitializeData(); var query = from c in categories join a in anotations on c.ID equals a.CategoryID select new { c.ID, c.Name, a.Anotation }; foreach (var item in query) { Console.WriteLine(item); }
実行結果は次のようになります。
{ ID = 1, Name = CS, Anotation = CSharp Programming }
{ ID = 2, Name = ASP, Anotation = ASP.NET Programming }
1.3 GroupJoin演算子
join演算の場合、結合するオブジェクトのリストに該当するデータのみが選択されますが、SQLのLEFT OUTER JOINと同じようにinnerで指定したリストと結合するオブジェクトがなくても、抽出対象としたい場合はjoin intoを使用します。対応する拡張メソッドはGroupJoinです。サンプルを掲載します。
var anotations = new[] { new { CategoryID = 1, Anotation = "CSharp Programming" }, new { CategoryID = 2, Anotation = "ASP.NET Programming" } }; var categories = InitializeData(); var query1 = from c in categories join a in anotations on c.ID equals a.CategoryID into anotationList select new { c.ID, c.Name, Anotations = anotationList, Count = anotationList.Count() }; foreach (var item in query1) { Console.WriteLine(item); foreach (var i in item.Anotations) { Console.WriteLine(i.Anotation); } }
処理結果が次のようになります。Join演算のサンプルと異なり、結合していないCategoryもselectの結果に含まれていることがわかります。
{ ID = 1, Name = CS, Anotations = System.Linq.Lookup`2+Grouping[System.Int32,<>f__AnonymousType1`2[System.Int32,System.String]], Count = 1 }
CSharp Programming
{ ID = 2, Name = ASP, Anotations = System.Linq.Lookup`2+Grouping[System.Int32,<>
f__AnonymousType1`2[System.Int32,System.String]], Count = 1 }
ASP.NET Programming
{ ID = 3, Name = SQL, Anotations = <>f__AnonymousType1`2[System.Int32,System.String][], Count = 0 }
{ ID = 4, Name = CS, Anotations = <>f__AnonymousType1`2[System.Int32,System.String][], Count = 0 }
1.4 集合演算Distinct,Union,Intersect,Except拡張メソッド
selectで選択されたリストのうち、ユニークなインスタンスのみのリストを返すのがDistinct()拡張メソッドです。既定では同一のオブジェクトかの判定はGetHashCodeとEqualsが使われますが、EqualityComparer<T>を使用すると、同一のオブジェクトかの判定方法をカスタマイズすることができます。
int[] values = new int[] { 4, 1, 2, 3, 4, 1, 2 }; var query = (from i in values select i).Distinct(); foreach (var item in query) { Console.WriteLine(item.ToString()); }
結果は次のようになります。
4
1
2
3
2つの集合の和集合を求めるのがUnion拡張メソッドです。EqualityComparer<T>を使用すると、オブジェクトの同一性の判定方法をカスタマイズできます。
int[] v1 = new int[] { 4, 1, 2, 3, 4, 1, 2 }; int[] v2 = new int[] { 1,7, 2, 5 }; var query = v1.Union(v2); foreach (var item in query) { Console.WriteLine(item.ToString()); }
結果は次のようになります。
4
1
2
3
7
5
2つの集合の求めるのがIntersect拡張メソッドです。EqualityComparer<T>を使用すると、オブジェクトの同一性の判定方法をカスタマイズできます。
int[] v1 = new int[] { 4, 1, 2, 3, 4, 1, 2 }; int[] v2 = new int[] { 1, 7, 2, 5 }; var query = v1.Intersect(v2); foreach (var item in query) { Console.WriteLine(item.ToString()); }
結果は次のようになります。
1
2
差集合(比較する集合に存在しない値の集合)を求めるのがExcept拡張メソッドです。EqualityComparer<T>を使用すると、オブジェクトの同一性の判定方法をカスタマイズできます。
int[] v1 = new int[] { 4, 1, 2, 3, 4, 1, 2 }; int[] v2 = new int[] { 1, 7, 2, 5 }; var query = v1.Except(v2); foreach (var item in query) { Console.WriteLine(item.ToString()); }
結果は次のようになります。
4
3
1.5 集約関数Count,LongCount,Sum,Min,Max,Average,Aggregate拡張メソッド
要素数をカウントする拡張メソッドがCount拡張メソッドです。返り値をlong型としたい場合はLongCount拡張メソッドを使用します。オーバーライド版を使用すると、要素をカウント対象に含めるかの判定ロジックを指定することができるようになります。
int[] v1 = new int[] { 4, 1, 2, 3, 4, 1, 2 }; var query = (from v in v1 where v >= 3 select v).Min(); Console.WriteLine(query.ToString());
3
要素の総和を求めるのがSum拡張メソッドです。selecterを指定するオーバーロード版を使用すると、総和を求める各要素の値を選択することができます。
int[] v1 = new int[] { 4, 1, 2, 3, 4, 1, 2 }; var query = (from v in v1 where v >= 3 select v).Sum(); Console.WriteLine(query.ToString());
実行結果は次のようになります。
11
集合のなかの最大値、最小値を求めるときに使用できるのがMax,Min拡張メソッドです。それぞれselecterを指定するオーバーロード版が用意されており、最大、最小を求める各要素の値を選択することができます。
var categories = InitializeData(); var query = (from c in categories from a in c.Articles select a).Max(a => a.ReleaseDate); Console.WriteLine(query.ToString());
int[] v1 = new int[] { 4, 1, 2, 3, 4, 1, 2 }; var query1 = v1.Min(); Console.WriteLine(query1.ToString());
実行は次のようになります。
2008/10/03 0:00:00
1
集合の平均を求めるのがAverage拡張メソッドです。Max,Minと同様に、selecterを指定するオーバーロード版を使用すると、平均を求めるのに使用する各要素の値を選択することができます。
var categories = InitializeData(); var query = (from c in categories from a in c.Articles select a).Average(a => a.ID); Console.WriteLine(query.ToString());
実行結果は次のようになります。
3.5
独自にカスタマイズした集約演算を行う場合は、Aggregate拡張メソッドを使用します。繰り返し呼び出される、Funcデリゲート内に集約操作を記述できます。オーバーロード版を使用すると、異なる型の演算結果を求めることができます。
var categories = InitializeData(); var query = categories.SelectMany(c => c.Articles).Aggregate((a1, a2) => (a1.ID > a2.ID) ? a1 : a2); Console.WriteLine(query.ToString()); var query1 = categories.SelectMany(c => c.Articles).Aggregate(0, (v, a) => v + a.ID); Console.WriteLine(query1.ToString());
実行結果は次のようになります。
Article:ID=6, Title=SqlArticle1, ReleaseDate=2008/10/03, ArticleType=Memo
21
今回のサンプルの掲載は以上です。
次のLinq To Objects備忘録3に続きます。
さんのコメント: さんのコメント: