初めまして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メソッドにもあるのでとても便利です。