LoginSignup
32
26

More than 5 years have passed since last update.

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

Posted at

【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は、要素全てに対して処理を行った結果を取り出します。

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

    // ----------------- foreach版--------------------
    // 結果を受け取る配列を用意
    var result1 = new int[points.Length];
    // ポインタを作成
    var i = 0;
    // 全て2倍して表示したい
    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

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

Where(選択)

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

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

    // ----------------- 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

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

Distinct

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

/// <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

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

Concat

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

/// <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

Concatで一発です。すごい。

Skip

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

/// <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

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

Take

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

/// <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

おまけ ToDictionary

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

/// <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

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

まとめ

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

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

32
26
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
32
26