うつつごころ

技術メモとか気になったこととか読んだ本とか

LINQ(to Object)のメソッドまとめ


こんにちは、うつつです。

今回はLINQについて。

業務でC#を扱うこと早半年…今まで他の人が書いたソースを見ながら何となくの理解でLINQを書いていました(…)。

が、先日やっとこさ調べてみたらこんなことも出来るの?!みたいなのがあってなかなか楽しかった&積極的に使いたいので備忘録的な感じでまとめておきます。


※注

  • LINQ to Object中心(これが理解できれば大体大丈夫な気がする)
  • サンプルは私の好みでメソッド式で記述
  • 出力には下記の出力用メソッドを使用
public static void WriteResult<T>(string tag, IEnumerable<T> e){
    Console.WriteLine(tag.PadRight(15) + String.Join(",", e));

}
public static void WriteResult<T>(string tag, T e){
    Console.WriteLine(tag.PadRight(15) + e);

}
public static void WriteResultRoop<T>(string tag, IEnumerable<T> e){
    Console.WriteLine(tag);
    foreach(var x in e)
    {
        Console.WriteLine(x);
    }
}


そもそもLINQ(統合言語クエリ)とは?

ざっくりまとめるとデータ集合の操作を簡単に行うことができるライブラリのようなもの。

using System.Linq;で使用可能。

C#以外でも様々な言語向けに開発が行われていて、javascriptPHPでも実装が可能。
 ex. javascriptjLinqとか

Javaでも使用出来るけどJava8からのStream APIが同様の機能ぽいので出番は…😴

クエリ式/メソッド式

LINQの構文はクエリ式とメソッド式の2種類あります。

クエリ式

var result = from number in numbers
                 where number < 50
                 select member;
 

メソッド式

var result = numbers.Where(x => x < 50).Selected(x => x);

 

ラムダ式

上記、メソッド式の例のメソッド内に書かれている式の構文をラムダ式と言います。

x => x < 50

簡略化された匿名型メソッドのことで、ここではxが50より小さいものを返し、Whereの条件にしています。
 

集計

// 最大値    
WriteResult("Max:", list.Max());

// 最小値 
WriteResult("Min", list.Min());

// 平均値
WriteResult("Average:",list.Average());

// 合計値
WriteResult("Sum:", list.Sum());

 // 要素数
WriteResult("Count:", list.Count());


配列化・リスト化

// 配列化
WriteResult("ToArray:", list.ToArray());

// リスト化
WriteResult("ToList:", list.ToList());


集合

// 重複を除く
WriteResult("Distinct", list.Distinct());

// 和集合
WriteResult("Union:", list1.Union(list2)); 
       
// 差集合
WriteResult("Except:", list1.Except(list2));

// 積集合
WriteResult("Intersect:", list1.Intersect(list2)); 


ソート

// 並び替え(昇順)
WriteResultRoop("OrderBy:", fruitList.OrderBy(x=> x.Price).Select(x => x.Name));
 
// 並び替え(降順) 
WriteResultRoop("OrderByDescending:", fruitList.OrderByDescending(x => x.Price));
       
// 逆順
WriteResultRoop("Reverse:", fruitList.Reverse());

 

条件指定

// 指定数スキップして残りを取得
WriteResult("Skip:", list.Skip(3));

// 先頭から指定数取得
WriteResult("Take:", list.Take(3));

// すべての要素が条件を満たしているか
WriteResult("All", list.All(x => x < 100));

// 条件を満たす要素が含まれているか
WriteResult("Any", list.Any(x => x < 0));

// 指定した要素が含まれているか
WriteResult("Contains:", list.Contains(50));


応用

// 重複を除き、要素を2つスキップした残りの要素を昇順に並び替える
WriteResultRoop("resultList1:", list.Distinct().Skip(2).OrderBy(x => x));
       
// 偶数を取得し、昇順で並び替え、配列に変換し、x*3して出力
WriteResultRoop("resultList2:", list.FindAll(x => x % 2 == 0).OrderBy(x => x).ToList().ConvertAll(x => x * 3));
       
// 上と同様の処理
WriteResultRoop("Where&Select:", list.Where(x => x % 2 == 0).OrderBy(x => x).Select(x => x * 3));


結合

// 内部結合
WriteResultRoop("InnerJoin:", fruitList1.Join(
       fruitList2,
       outer => outer.Name,
       inner => inner.Name,
       (outer, inner) => new {
           outer.Name,
           outer.Price,
           inner.Stock
       }));

// 外部結合
WriteResultRoop("OuterJoin:", fruitList1.GroupJoin(
       fruitList2,
       outer => outer.Name,
       inner => inner.Name,
       (outer, inner) => new {
           outer.Name,
           outer.Price,
           Stock = inner.Select(x => x.Stock).Sum()

       }));
       
// 結合
WriteResultRoop("ConCat:", fruitList1.Concat(fruitList3));


まとめ 

実際に使用して個人的に感じているメリットとしては以下

  • DBの能力に依存しないため処理速度が向上する(こともある)。
  • LINQを使用しない場合と比べて簡潔な記述になる(ことが多い)。
  • ソース解析が楽。


特に、適切に使用した際ソースの可読性が上がる点がとても好きです(ある程度の理解が大前提になってしまいますが)。

DBのテーブルデータに大量の条件を付けて他のテーブルデータと結合してそれをまた…みたいに延々とやるとめちゃくちゃ読みづらそうですが(というかもうそれはSQLに任せたい)、
簡単なデータ操作はLINQを使って読みやすいコードにしていくのがいいと思いました!

以上、うつつがお送りしました。