LINQって使ってますか?コレクションの要素を操作する場合に、簡潔に記述することができて便利です。
この記事では、LINQについて
- LINQについて
- Selectメソッドの使い方
- Groupメソッドの使い方
- Count、Max、Sumで集計する方法
- Any、Allで要素をチェックする方法
- LINQ to XMLでHTML、XMLの解析
など基本的な内容から、応用的な使い方についても解説していきます。今回はLINQについて、使い方をわかりやすく解説します。
LINQについて
LINQとはコレクション(配列やList、Dictionaryなど)の要素を処理するメソッドを集めたライブラリです。LINQを使うと、for文やforeach文を使ったループ処理を簡潔に置き換えることができます。LINQを使うためには、usingディレクティブを使ってSystem.Linqを参照する必要があります。
ラムダ式とは
LINQを使って簡潔に記述するためにはラムダ式を使います。ラムダ式とは、一言で言うと「メソッドを変数と同様に扱う記述様式」になります。ラムダ式の基本構文は以下のようになります。
左辺 => 右辺
パラメータは「( )」で囲む必要がありますが、変数が1つのみの場合は「( )」を省略することができます。変数が複数の場合は、各変数を「,」(カンマ)で区切ります。
() => 右辺 // 変数なしの場合 param => 右辺 // 変数が1つの場合 (param1, param2) => 右辺 // 変数が2つの場合
変数の型はコンパイラによって自動的に推論されます。
必要であれば型を指定することもできます。
(int param) => 右辺 // 変数をint型で指定
Selectメソッドの使い方
LINQのSelectメソッドは、コレクションの要素全てを処理して別のオブジェクトに渡すときに使用します。ラムダ式の右辺に処理を記述します。サンプルコードで確認しましょう。
using System; using System.Linq; namespace Sample { class Sample { static void Main() { int[] src = {0, 1, 2, 3, 4, 5}; var query = src.Select(x => x % 3); Console.WriteLine("[{0}]", string.Join(", ", query)); Console.ReadKey(); } } }
実行結果:
[0, 1, 2, 0, 1, 2]
このサンプルコードでは、Selectメソッドを使ってint型の配列srcの要素を処理しています。要素の処理として、3で割った余りを算出しています。このサンプルコードのように、for文やforeach文を使って要素を1つずつ処理する必要がなく簡潔に記述することができます。
Whereオペレータと組み合わせで使う方法
Whereオペレータを使うことで、条件を満足する要素のみSelectメソッドで処理することができます。Whereオペレーションの引数もSelectメソッドの引数と同じようにラムダ式で記述します。サンプルコードで確認しましょう。
using System; using System.Linq; namespace Sample { class Sample { static void Main() { int[] src = {0, 1, 2, 3, 4, 5}; var query = src .Where(x => x > 1) .Select(x => x % 3); Console.WriteLine("[{0}]", string.Join(", ", query)); Console.ReadKey(); } } }
実行結果:
[2, 0, 1, 2]
このサンプルコードでは、Whereオペレーションを使って配列srcの要素から1より大きな要素のみSelectメソッドで処理を行っています。
foreach文の置き換え
SelectメソッドやWhereオペレータを使った記述についてご紹介してきました。これをforeach文で書くとどのようになるかご紹介しておきます。
using System; namespace Sample { class Sample { static void Main() { int[] src = {0, 1, 2, 3, 4, 5}; foreach(int num in src) { if(num > 1 && num < 5) { int mod = num % 3; Console.WriteLine("{0}", mod); } } Console.ReadKey(); } } }
実行結果:
2 0 1
LINQのSelectメソッドやWhereオペレータを使う場合に比べてやはり記述の手間が増えていますね。
SelectManyメソッドの使い方
Selectメソッドと似た処理を行うSelectManyメソッドがあります。SelectManyメソッドを使うと1つのシーケンスに平坦化することができます。平坦化についてサンプルコードを使って解説します。
using System; using System.Collections.Generic; using System.Linq; namespace Sample { class Quarter { public string Period { get; set; } public List<String> Months { get; set; } } class Sample { static void Main() { Quarter[] quarter = { new Quarter{Period = "1st", Months = new List<string>{"Jan", "Feb", "Mar"}}, new Quarter{Period = "2nd", Months = new List<string>{"Apr", "May", "Jun"}}, new Quarter{Period = "3rd", Months = new List<string>{"Jul", "Aug", "Sep"}}, new Quarter{Period = "4th", Months = new List<string>{"Oct", "Nov", "Dec"}} }; var query = quarter.SelectMany(x => x.Months); Console.WriteLine("[{0}]", string.Join(", ", query)); Console.ReadKey(); } } }
実行結果:
[Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec]
SelectManyメソッドを使うことで、配列quarterの要素内のListの要素を全て取得することができています。このようにコレクションの要素内の要素を全て同じコレクションの要素に格納することを平坦化と言います。
ここまでLINQのSelectメソッドやWhereオペレータの使い方について解説してきました。こちらではさらに詳しく解説していますので、ぜひ参考にしてください。
GroupByメソッドの使い方
GroupByメソッドを使うと、コレクションの要素をグループに分類する場合に簡潔に記述することができて便利です。GroupByメソッドはコレクションのオブジェクトから呼び出して使うことができます。GroupByメソッドの引数にはラムダ式でグループ分けのKeyを指定します。
GroupByソッドの使い方について、サンプルコードで確認しましょう。
using System; using System.Collections.Generic; using System.Linq; namespace Sample { class Test { public string Subj { get; set; } public int Points { get; set; } public string Name { get; set; } public string ClassName { get; set; } } class Sample { static void Main() { var result = new List<Test>() { new Test {Subj = "国語", Points = 90, Name = "田中 一郎", ClassName = "A"}, new Test {Subj = "数学", Points = 80, Name = "田中 一郎", ClassName = "A"}, new Test {Subj = "英語", Points = 70, Name = "田中 一郎", ClassName = "A"}, new Test {Subj = "国語", Points = 60, Name = "鈴木 二郎", ClassName = "A"}, new Test {Subj = "数学", Points = 50, Name = "鈴木 二郎", ClassName = "A"}, new Test {Subj = "英語", Points = 80, Name = "鈴木 二郎", ClassName = "A"}, new Test {Subj = "国語", Points = 70, Name = "佐藤 三郎", ClassName = "B"}, new Test {Subj = "数学", Points = 80, Name = "佐藤 三郎", ClassName = "B"}, new Test {Subj = "英語", Points = 90, Name = "佐藤 三郎", ClassName = "B"}, }; var query = result.GroupBy(x => x.Subj); foreach(var group in query) { Console.WriteLine(group.Key); foreach(var item in group) { Console.WriteLine("{0}:{1}点", item.Name, item.Points); } } Console.ReadKey(); } } }
実行結果:
国語 田中 一郎:90点 鈴木 二郎:60点 佐藤 三郎:70点 数学 田中 一郎:80点 鈴木 二郎:50点 佐藤 三郎:80点 英語 田中 一郎:70点 鈴木 二郎:80点 佐藤 三郎:90点
このサンプルコードでは、GroupByメソッドを使ってTestクラスのメンバ―変数Subjの値でグループ分けをしています。
OrderByメソッドとの組み合わせで使う方法
LINQのOrderByメソッドは、コレクションの要素を並び替える場合に使用します。OrderByメソッドもコレクションのオブジェクトから呼び出して使うことができます。OrderByメソッドの引数にはラムダ式で並び替えのKeyを指定します。
昇順に並び替える場合はOrderByメソッドを使用し、降順に並び替える場合はOrderByDescendingメソッドを使用します。
using System; using System.Collections.Generic; using System.Linq; namespace Sample { class Test { public string Subj { get; set; } public int Points { get; set; } public string Name { get; set; } public string ClassName { get; set; } } class Sample { static void Main() { var result = new List<Test>() { new Test {Subj = "国語", Points = 90, Name = "田中 一郎", ClassName = "A"}, new Test {Subj = "数学", Points = 80, Name = "田中 一郎", ClassName = "A"}, new Test {Subj = "英語", Points = 70, Name = "田中 一郎", ClassName = "A"}, new Test {Subj = "国語", Points = 60, Name = "鈴木 二郎", ClassName = "A"}, new Test {Subj = "数学", Points = 50, Name = "鈴木 二郎", ClassName = "A"}, new Test {Subj = "英語", Points = 80, Name = "鈴木 二郎", ClassName = "A"}, new Test {Subj = "国語", Points = 70, Name = "佐藤 三郎", ClassName = "B"}, new Test {Subj = "数学", Points = 80, Name = "佐藤 三郎", ClassName = "B"}, new Test {Subj = "英語", Points = 90, Name = "佐藤 三郎", ClassName = "B"}, }; var query = result .OrderByDescending(x => x.Points) .GroupBy(x => x.Subj); foreach(var group in query) { Console.WriteLine(group.Key); foreach(var item in group) { Console.WriteLine("{0}:{1}点", item.Name, item.Points); } } Console.ReadKey(); } } }
実行結果:
国語 田中 一郎:90点 佐藤 三郎:70点 鈴木 二郎:60点 英語 佐藤 三郎:90点 鈴木 二郎:80点 田中 一郎:70点 数学 田中 一郎:80点 佐藤 三郎:80点 鈴木 二郎:50点
このサンプルコードでは、OrderByDescendingメソッドを使ってTestクラスのメンバ変数Pointsの値で降順に並び替えています。ここまでLINQのGroupByメソッド、OrderByメソッドの使い方について説明してきました。これらについては、こちらで詳しく解説していますので、ぜひ参考にしてください。
Count、Max、Sumで集計する方法
Count、Max、Sumなどの集計演算子を使ってグループ分けした結果の値を集計演算することができます。集計演算子には以下のようにいくつかの種類があります。
集計演算子 | 説明 |
---|---|
Max | 最大値を返す |
Min | 最小値を返す |
Average | 平均値を返す |
Sum | 合計値を返す |
Count | 要素数を返す |
ここではMaxの使い方について、サンプルコードで確認しましょう。
using System; using System.Collections.Generic; using System.Linq; namespace Sample { class Test { public string Subj { get; set; } public int Points { get; set; } public string Name { get; set; } public string ClassName { get; set; } } class Sample { static void Main() { var result = new List<Test>() { new Test {Subj = "国語", Points = 90, Name = "田中 一郎", ClassName = "A"}, new Test {Subj = "数学", Points = 80, Name = "田中 一郎", ClassName = "A"}, new Test {Subj = "英語", Points = 70, Name = "田中 一郎", ClassName = "A"}, new Test {Subj = "国語", Points = 60, Name = "鈴木 二郎", ClassName = "A"}, new Test {Subj = "数学", Points = 50, Name = "鈴木 二郎", ClassName = "A"}, new Test {Subj = "英語", Points = 80, Name = "鈴木 二郎", ClassName = "A"}, new Test {Subj = "国語", Points = 70, Name = "佐藤 三郎", ClassName = "B"}, new Test {Subj = "数学", Points = 80, Name = "佐藤 三郎", ClassName = "B"}, new Test {Subj = "英語", Points = 90, Name = "佐藤 三郎", ClassName = "B"}, }; var query = result .GroupBy(x => x.Subj) .Select(x => new {Subj = x.Key, Max = x.Max(y => y.Points)}); foreach(var group in query) { Console.WriteLine("{0}の最高点:{1}点", group.Subj, group.Max); } Console.ReadKey(); } } }
実行結果:
国語の最高点:90点 数学の最高点:80点 英語の最高点:90点
このサンプルコードでは、集合演算子Maxを使って各教科の最高得点を演算算出しています。また、その他の集計演算子CountやSumの使い方については、こちらで詳しく解説しています。ぜひ参考にしてください。
Any、Allで要素をチェックする方法
LINQのAnyメソッドやAllメソッドを使うと、コレクションの要素が条件を満たしているか判定することができます。
Anyで条件を満たす要素が一つでもあるかチェック
Anyメソッドを使うと、条件を満たす要素が一つでも存在するかチェックすることができます。
サンプルコードで確認しましょう。
using System; using System.Collections.Generic; using System.Linq; namespace Sample { class Test { public string Subj { get; set; } public int Points { get; set; } public string Name { get; set; } public string ClassName { get; set; } } class Sample { static void Main() { var result = new List<Test>() { new Test {Subj = "国語", Points = 90, Name = "田中 一郎", ClassName = "A"}, new Test {Subj = "数学", Points = 80, Name = "田中 一郎", ClassName = "A"}, new Test {Subj = "英語", Points = 70, Name = "田中 一郎", ClassName = "A"}, new Test {Subj = "国語", Points = 60, Name = "鈴木 二郎", ClassName = "A"}, new Test {Subj = "数学", Points = 50, Name = "鈴木 二郎", ClassName = "A"}, new Test {Subj = "英語", Points = 80, Name = "鈴木 二郎", ClassName = "A"}, new Test {Subj = "国語", Points = 70, Name = "佐藤 三郎", ClassName = "B"}, new Test {Subj = "数学", Points = 80, Name = "佐藤 三郎", ClassName = "B"}, new Test {Subj = "英語", Points = 90, Name = "佐藤 三郎", ClassName = "B"}, }; var query = result .GroupBy(x => x.Subj) .Select(x => new {Subj = x.Key, Bool = x.Any(y => y.Points < 70)}); foreach(var group in query) { if(group.Bool) { Console.WriteLine("{0}では70点未満がいます", group.Subj); } else { Console.WriteLine("{0}では70点未満はいません", group.Subj); } } Console.ReadKey(); } } }
実行結果:
国語では70点未満がいます 数学では70点未満がいます 英語では70点未満はいません
このサンプルコードでは、Anyメソッドを使って各教科で70点未満が存在するかチェックしています。
Allで要素全てが条件を満たすかチェック
Allメソッドを使うと、全ての要素が条件を満たすかチェックすることができます。
サンプルコードで確認しましょう。
using System; using System.Collections.Generic; using System.Linq; namespace Sample { class Test { public string Subj { get; set; } public int Points { get; set; } public string Name { get; set; } public string ClassName { get; set; } } class Sample { static void Main() { var result = new List<Test>() { new Test {Subj = "国語", Points = 90, Name = "田中 一郎", ClassName = "A"}, new Test {Subj = "数学", Points = 80, Name = "田中 一郎", ClassName = "A"}, new Test {Subj = "英語", Points = 70, Name = "田中 一郎", ClassName = "A"}, new Test {Subj = "国語", Points = 60, Name = "鈴木 二郎", ClassName = "A"}, new Test {Subj = "数学", Points = 50, Name = "鈴木 二郎", ClassName = "A"}, new Test {Subj = "英語", Points = 80, Name = "鈴木 二郎", ClassName = "A"}, new Test {Subj = "国語", Points = 70, Name = "佐藤 三郎", ClassName = "B"}, new Test {Subj = "数学", Points = 80, Name = "佐藤 三郎", ClassName = "B"}, new Test {Subj = "英語", Points = 90, Name = "佐藤 三郎", ClassName = "B"}, }; var query = result .GroupBy(x => x.Subj) .Select(x => new {Subj = x.Key, Bool = x.All(y => y.Points >= 70)}); foreach(var group in query) { if(!group.Bool) { Console.WriteLine("{0}では70点未満がいます", group.Subj); } else { Console.WriteLine("{0}では70点未満はいません", group.Subj); } } Console.ReadKey(); } } }
実行結果:
国語では70点未満がいます 数学では70点未満がいます 英語では70点未満はいません
このサンプルコードでは、Allメソッドを使って各教科で70点未満が存在するかチェックしています。
LINQ to XMLでHTML、XMLの解析
C#ではLINQ to XMLという技術を使うことで、HTMLやXMLドキュメントを簡単に解析することもできます。
サンプルコードを使って確認していきましょう。
using System; using System.Linq; using System.Xml.Linq; namespace Sample { class Sample { static void Main() { string xmlDoc = @"<?xml version='1.0'?> <names xmlns='https://www.sejuku.net/example/linqtoxml'> <name class='A'>田中 一郎</name> <name class='A'>鈴木 二郎</name> <name class='B'>佐藤 三郎</name> </names>"; var doc = XElement.Parse(xmlDoc); XNamespace xns = "https://www.sejuku.net/example/linqtoxml"; var query = from n in doc.Descendants(xns + "name") where n.Attribute("class").Value.StartsWith("A") select n; foreach(var group in query) { Console.WriteLine(group.Value); } Console.ReadKey(); } } }
実行結果:
田中 一郎 鈴木 二郎
このサンプルコードでは、class属性の値が「A」で始まるnameタグの内容を出力しています。このサンプルコードではXMLドキュメントの解析を行いましたが、同じようにしてHTMLドキュメントの解析も可能です。
まとめ
ここではLINQについて説明しました。コレクションの要素を操作する場合に、簡潔に記述することができて便利ですよね。
使いこなすことができるように、この記事を何度も参考にして下さいね!