初めましてkouと言います。
昨年夏から一年ちょいC#にて仕事をしていました。
C#を書いていて、これを最初に教えてくれる人がいたらなぁとか、これを先に知ってたらなぁとか、
そういうった事をLinqメインにざっとまとめてみました。
LinqというはSQL、MXL、Objectに対してSQLライクな文法でアクセスするものです。
今回Linqの説明は省きますが、よくあるfilter,map,なんかをSQLみたいな感じで書けるものです。
多重ループをクエリ式にする
for文のネストが深いと嫌ですよね。
foreachで書かれたものであれば、クエリ式を使う事で簡単に深いネストをさける事ができます。
public static void TestLinq() { List<string> testList = new List<string>(); testList.Add("test"); testList.Add("sample"); testList.Add("hoge"); testList.Add("ex"); testList.Add("etc"); foreach (var test in testList) { foreach (var i in Enumerable.Range(1, 5)) { System.Console.WriteLine("{0} : {1}", test, i); } } }
foreachの括弧内を抜き出して頭にfromをつけ
最終的に欲しいものをselect文の中に匿名型で書くだけでOKです。
ネストが増えたらその分fromを増すだけです。
public static void TestLinq2() { List<string> testList = new List<string>(); testList.Add("test"); testList.Add("sample"); testList.Add("hoge"); testList.Add("ex"); testList.Add("etc"); var resultStrs = from test in testList from i in Enumerable.Range(1, 5) select new { test, i }; resultStrs.ToList().ForEach(System.Console.WriteLine); }
こういう簡単なケースに対応できるのはいいとして、実際には各ループの最中に色々処理があったりすると思います。
こんな感じで。
public static void TestLinqEx() { List<string> testList = new List<string>(); testList.Add("test"); testList.Add("sample"); testList.Add("hoge"); testList.Add("ex"); testList.Add("etc"); foreach (var test in testList) { var addStr = test + " new version"; foreach (var i in Enumerable.Range(1, 5)) { var addStr2 = test + ":" + i.ToString(); System.Console.WriteLine("{0} : {1}", addStr, addStr2); } } }||< こういう場合にどうやって対処するかというと、let句を使って対処します。 各ループ内の処理の頭のvarをletに変えて文末の;を取ればOKです。 処理が増える分let句が増えていく感じです。 if,break,continue等入ってないループに関してはこの方法で深いネストを解消できると思います。 今回はForEachメソッドを使わないでforeachで回して出力しています。 匿名型へのアクセスはこんな感じになります。 >|c-sharp| public static void TestLinq2() { List<string> testList = new List<string>(); testList.Add("test"); testList.Add("sample"); testList.Add("hoge"); testList.Add("ex"); testList.Add("etc"); var resultStrs = from test in testList from i in Enumerable.Range(1, 5) select new { test, i }; resultStrs.ToList().ForEach(System.Console.WriteLine); }
便利なオーバーロード
Linqの各種メソッドには便利なオーバーロードがあります。
大抵の場合Select,Whereを使ってから何かする場合のSelect,Whereの部分は省略できます。
Selectを省略できるパターン
以下のコードのpriceListは何かの商品レコードのような物が入っているListだと思ってください。
その商品の中のカテゴリーが3の物の合計値を取得するみたいなプログラムを書いてみました。
public static void TestSum() { List<int> priceList = new List<int>(); priceList.Add(1000); priceList.Add(2000); priceList.Add(5000); priceList.Add(10000); var results = from price in priceList from category in Enumerable.Range(1, 5) select new { price, category }; // selectいらない //var sum = results.Where(x => x.category == 3).Select(x => x.price).Sum(); var sum = results.Where(x => x.category == 3).Sum(x => x.price); System.Console.WriteLine(sum);
Whereを省略できるパターン
public static void TestFirst() { List<int> priceList = new List<int>(); priceList.Add(1000); priceList.Add(2000); priceList.Add(5000); priceList.Add(10000); var results = from price in priceList from category in Enumerable.Range(1, 5) select new { price, category }; // whereいらない //var firstPrice = results.Where(x => x.category == 3).Select(x => x.price).First().price; var firstPrice = results.First(x => x.category == 3).price; System.Console.WriteLine(firstPrice); }
実際の所はクエリ式の部分のfrom price in priceList.Where(x => x.category == 3)
した方が効率いいのですが、オーバーロードの説明したかったのでこうなってます。
ループのインデックスを取得したい
foreachやクエリ式を使ってるとループのインデックスを取得したくなる事が多いと思います。
foreachのスコープ外にindexとか変数作ってループ内でインクリメントとかしちゃってましたが
Linqではそんな事しないでも簡単にインデックスを取得できます。
public static void TestIndex() { List<string> testList = new List<string>(); testList.Add("test"); testList.Add("sample"); testList.Add("hoge"); testList.Add("ex"); testList.Add("etc"); foreach (var test in testList.Select((x, i) => new { x, i })) System.Console.WriteLine("{0} : {1}", test.x, test.i); }
これでiの部分にインデックスが入ってきます、こんな感じのオーバーロードは各種Linqメソッドにもあるのでとても便利です。