【C#】LINQ備忘録1 ～初級編～

# 【C#】LINQ備忘録1 ～初級編～
先月C#の案件をこなしている中で、LINQを触る機会があったのでメモ。
と～～～～っても使いやすくて、素敵な構文でした。C#すごいわほんと。
LINQについてはかなりの数の記事がありますので、あくまでこれは私の備忘録としてメモしておきます。

## LINQって？
>統合言語クエリ (LINQ, Language INtegrated Query, リンクと発音する)とは、.NET Framework 3.5において、様々な種類のデータ集合に対して標準化された方法でデータを問い合わせる（クエリ）ことを可能にするために、言語に統合された機能のことである。開発ツールはVisual Studio 2008から対応している。

今回の開発で用いたのは C#での__LINQ to Object__というヤツで、よくある形のforやforeach文で処理を行うのではなく、
一つの要素のカタマリに対してクエリを投げることで、射影（Select）を行ったり、選択（Where）を行ったりできます。

Java7でずーっと育ってきた私としては、中々このLINQの書き方には慣れ込めず、思ったよりも
コーディングに時間が掛かってしまいましたが、出来上がったコードはとーってもリーダブルで、
読みやすく、整ったコードになったと思います。余計な変数が生まれない高階関数って素晴らしいっすわ。

まだまだ勉強不足なところはありますが

* Select
* Where
* Distinct
* Concat
* Skip
* Take

あたりまでは使いこなせるようになったので、それぞれ備忘録としてササッと残しておきたいと思います。

## Select（射影）
Selectは、要素全てに対して処理を行った結果を取り出します。

~~~C#
/// <summary>
/// メインエントリ
/// </summary>
/// <param name="args"></param>
static void Main(string[] args)
{
    // コレクションを用意
    var points = new[] { 20, 30, 40, 40, 50 };
    // 全て２倍した集合がほしい！

    // ----------------- foreach版--------------------
    // 結果を受け取る配列を用意
    var result1 = new int[points.Length];
    // ポインタを作成
    var i = 0;
    // 全て２倍して表示したい
    foreach (var p in points)
    {
        // 配列に2倍した値を入力
        result1[i] = p * 2;
        // ポインタを更新
        i++;
    }
    //画面に表示
    Console.WriteLine(string.Join(",", result1));

    // ----------------- Linq版--------------------
    // 2倍した集合を取得
    var result2 = points.Select(x => x * 2).ToArray();
    //画面に表示
    Console.WriteLine(string.Join(",", result2));
}
~~~
__表示結果__
![image](https://qiita-image-store.s3.amazonaws.com/0/131683/eff2fbda-fcb2-63af-568e-4c969953b663.png)




従来のforeachを使ったコードよりも、圧倒的に行数が少なくなります。
Selectでは、要素それぞれに対して値を２倍し、それらの集合を取得しています。
今回、foreachのコードと比較するためToArray()を終端処理として入れていますが、
単に表示するだけであれば、これも必要ありません。

## Where（選択）
Whereは、要素の中から指定した条件に合致するものを取り出します。

~~~C#
/// <summary>
/// メインエントリ
/// </summary>
/// <param name="args"></param>
static void Main(string[] args)
{
    // コレクションを用意
    var points = new[] { 20, 30, 40, 40, 50 };
    // ４０以上の値だけ抜き出したい！

    // ----------------- foreach版--------------------

    // 結果を受け取るリストを用意
    var result1 = new List<int>();
    // 40以上の値だけ抜き出したい！
    foreach (var p in points)
    {
        if(p >= 40)
        {
            // リストに値を追加
            result1.Add(p);
        }
    }
    //画面に表示
    Console.WriteLine(string.Join(",", result1));

    // ----------------- Linq版--------------------
    // 40以上の値だけ抜き出したい！
    var result2 = points.Where(x => x >= 40).ToList();
    //画面に表示
    Console.WriteLine(string.Join(",", result2));
}
~~~
__表示結果__
![image](https://qiita-image-store.s3.amazonaws.com/0/131683/6db0c573-ddc9-bf3b-eeb6-35f00990c38a.png)


Selectと同様にArrayでやろうと思ったんですけど、foreach版が面倒になったのでListで取得することにします。
LINQ版は、Whereで40以上の条件指定し、それに合致するもののみ選択して取得しています。
終端処理はforeach版に合わせてListにしています。
これも、行数が削減され、可読性が上がっているのがわかると思います。
LINQは１行１行が意味のある処理となるのがいいですね。

## Distinct
Distinctは、要素の中から重複しているものを排除します。

~~~C#
/// <summary>
/// メインエントリ
/// </summary>
/// <param name="args"></param>
static void Main(string[] args)
{
    // コレクションを用意
    var points = new[] { 20, 30, 40, 40, 50 };
    // 重複を排除したい！

    // ----------------- foreach版--------------------

    // 結果を受け取るリストを用意
    var result1 = new List<int>();
    // 重複を排除したい！
    foreach (var p in points)
    {
        // すでに追加されていない場合
        if(!result1.Contains(p))
        {
            // リストに値を追加
            result1.Add(p);
        }
    }
    //画面に表示
    Console.WriteLine(string.Join(",", result1));

    // ----------------- Linq版--------------------
    // 重複を排除したい！
    var result2 = points
        .Distinct()
        .ToList();
    //画面に表示
    Console.WriteLine(string.Join(",", result2));
}
~~~
__表示結果__
![image](https://qiita-image-store.s3.amazonaws.com/0/131683/1f202b06-80e1-7e2e-bcb1-0ee35f4a7da2.png)

このメソッド、超便利っす。何も考えずにSQLのDistinctと同じ形で使えるし、
見栄えもとってもいいですね。

## Concat
Concatは、要素と要素を連結させます。

~~~C#
/// <summary>
/// メインエントリ
/// </summary>
/// <param name="args"></param>
static void Main(string[] args)
{
    // コレクションを用意
    var points1 = new[] { 20, 30, 40, 40, 50 };
    // コレクションを用意
    var points2 = new[] { 60, 70, 70, 80 };
    // くっつける！

    // ----------------- foreach版--------------------
    // 結果を受け取るリストを用意
    var result1 = new List<int>();
    // くっつける！
    foreach (var p in points1)
    {
        // points1の値を入れる
        result1.Add(p);
    }
    // くっつける！
    foreach (var p in points2)
    {
        // points2の値を入れる
        result1.Add(p);
    }
    //画面に表示
    Console.WriteLine(string.Join(",", result1));

    // ----------------- Linq版--------------------
    // くっつける！
    var result2 = points1.Concat(points2).ToList();
    //画面に表示
    Console.WriteLine(string.Join(",", result2));
}
~~~
__表示結果__
![image](https://qiita-image-store.s3.amazonaws.com/0/131683/8d9053a6-4b0d-a8b8-0b5d-57c4f8b0ecdc.png)

Concatで一発です。すごい。

## Skip
Skipは、要素を指定した数だけ読み飛ばします。

~~~C#
/// <summary>
/// メインエントリ
/// </summary>
/// <param name="args"></param>
static void Main(string[] args)
{
    // コレクションを用意
    var points = new[] { 20, 30, 40, 40, 50 };
    // 3つ飛ばして4つ目から見たい！

    // ----------------- foreach版--------------------
    // 結果を受け取るリストを用意
    var result1 = new List<int>();
    // 3つ飛ばして4つ目から見たい！
    for (int i = 3; i < points.Length; i++)
    {
        // pointsの値を入れる
        result1.Add(points[i]);
    }
    //画面に表示
    Console.WriteLine(string.Join(",", result1));

    // ----------------- Linq版--------------------
    // 3つ飛ばして4つ目から見たい！
    var result2 = points.Skip(3).ToList();
    //画面に表示
    Console.WriteLine(string.Join(",", result2));
}
~~~
__表示結果__
![image](https://qiita-image-store.s3.amazonaws.com/0/131683/9239df4b-9b8c-4634-d2bb-a893c03d12dc.png)

インデックスに関連する処理は、for文を使う必要があり、面倒なコードが増えてしまいます。
LINQと比較すると、差は歴然ですね。使いましょう。Skip。

## Take
Takeは、要素を指定した数だけ読みます。
Skipと組み合わせると、範囲指定することもできます。

~~~C#
/// <summary>
/// メインエントリ
/// </summary>
/// <param name="args"></param>
static void Main(string[] args)
{
    // コレクションを用意
    var points = new[] { 20, 30, 40, 40, 50 };
    // 2つ目から4つ目まで見たい!

    // ----------------- foreach版--------------------
    // 結果を受け取るリストを用意
    var result1 = new List<int>();
    // 2つ目から4つ目まで見たい!
    for (int i = 1; i < 4; i++)
    {
        // pointsの値を入れる
        result1.Add(points[i]);
    }
    //画面に表示
    Console.WriteLine(string.Join(",", result1));

    // ----------------- Linq版--------------------
    // 2つ目から4つ目まで見たい!
    var result2 = points.Skip(1).Take(3).ToList();
    //画面に表示
    Console.WriteLine(string.Join(",", result2));

    // ----------------- 表示--------------------
    Console.ReadLine();
}
~~~
__表示結果__
![image](https://qiita-image-store.s3.amazonaws.com/0/131683/71ed64f7-d0e1-ac1c-f8e7-a79eca042cab.png)

## おまけ ToDictionary
ToDictionaryは、Selectして得られる結果をDictionaryとして受け取ります。

~~~C#
/// <summary>
/// メインエントリ
/// </summary>
/// <param name="args"></param>
static void Main(string[] args)
{
    // コレクションを用意
    var productList = new Dictionary<string, int> {
        { "りんご", 200 },
        { "バナナ", 150 },
        { "すいか", 150 },
        { "ぶどう", 400 },
    };
    // 全てのValueを+50したDictionaryがほしい！

    // ----------------- foreach版--------------------
    // 結果を受け取るDictionaryを作成
    var result1 = new Dictionary<string, int>();
    // 全てのValueを+50したDictionaryを取得
    foreach (var i in productList)
    {
        result1.Add(i.Key, i.Value + 50);
    }
    ShowConsole(result1);
    // ----------------- Linq版--------------------
    // 全ての要素を+50したDictionaryを取得
    var result2 = productList
        .ToDictionary(x => x.Key, x => x.Value + 50);
    ShowConsole(result2);
}

/// <summary>
/// 表示用メソッド
/// </summary>
/// <param name="elements">表示するDictionary</param>
static void ShowConsole(Dictionary<string,int> elements)
{
    // Key、Valueのペアを表示
    Console.WriteLine(string.Join("\n", elements.Select(x => {
        return x.Key + "\t" + x.Value;
    })));
}
~~~
__表示結果__
![image](https://qiita-image-store.s3.amazonaws.com/0/131683/3e017bf0-e946-a651-e823-c309ed429be7.png)

Key、Valueそれぞれに対してSelectし、Dictionary化します。
実務上ではわざわざToDictionary()を加えて辞書にする必要は少ないかもしれませんが、
覚えておきたいと思います。

## まとめ
SelectManyやToLookUpなど、他にも幾つか覚えることができたものがあるので、
そちらも近いうちに備忘録化したいと思っています。

LINQ素晴らしい！またC#案件が来るのを楽しみに待っていようと思います。