【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));
}
従来の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));
}
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));
}
このメソッド、超便利っす。何も考えずに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));
}
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));
}
インデックスに関連する処理は、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();
}
おまけ 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;
})));
}
Key、Valueそれぞれに対してSelectし、Dictionary化します。
実務上ではわざわざToDictionary()を加えて辞書にする必要は少ないかもしれませんが、
覚えておきたいと思います。
まとめ
SelectManyやToLookUpなど、他にも幾つか覚えることができたものがあるので、
そちらも近いうちに備忘録化したいと思っています。
LINQ素晴らしい!またC#案件が来るのを楽しみに待っていようと思います。