こんにちは。明月です。
この投稿はC#のLinqクエリ式を使い方に関する説明です。
以前の投稿でLinq式に関して簡単に説明したことがあります。
link - [C#] 28. リスト(List)とディクショナリ(Dictionary)、そしてLinq式を使い方
また、簡単に説明するとLinqとはプログラム上で設定されたオブジェクトの集合、つまりリスト(List)やディクショナリ(Dictionary)で設定されたデータを効果的に分類及び検索するためのC#のプログラム文法です。
Linqを作成する方法はクエリ式と関数式がありますが、その中でクエリ式はデータベースで使うSQLクエリ式と似ている方法です。
基本的に使う方法はfrom in where selectです。
using System;
using System.Collections.Generic;
using System.Linq;
namespace Example
{
// 例クラス
class Node
{
// 値をコンストラクタで格納する。
public Node(int data)
{
// プロパティのDataに値を格納
this.Data = data;
}
// Dataプロパティ
public int Data
{
// 入力はコンストラクタから受け取る。
get; private set;
}
}
class Program
{
// 実行関数
static void Main(string[] args)
{
// リスト宣言(リストのオブジェクトはNodeクラス)
var list = new List<Node>();
// iが0から9まで繰り返し
for (int i = 0; i < 10; i++)
{
// リストにデータを挿入
list.Add(new Node(i));
}
// NodeクラスのData値が6以上のインスタンスをList<int>タイプで分類する。
var filterList = from node in list where node.Data > 5 select node.Data;
// フィルターされたリストを繰り返しで抽出
foreach (var node in filterList)
{
// コンソール出力
Console.WriteLine(node);
}
// 任意のキーを押してください
Console.WriteLine("Press any key...");
Console.ReadLine();
}
}
}
まず、from inに関して説明するとinの後の変数は検索しようと思うリストの変数です。
そしてforeachの繰り返し文みたいに一つのオブジェクトに置換することがfromの後の変数です。
つまり、foreach(Node node in list)がfrom node in listと同じ意味になります。
whereは繰り返し文の中で条件を作ることでnode.Data > 5はif(node.Data > 5)と同じ意味です。
selectはreturnされた結果により最終のfilterListのデータタイプが決められます。select nodeをしたらIEnumerable<Node>のタイプになりますが、上の例ではnode.DataをしたのでIEnumerable<int>タイプで生成されます。
ここで参考にIEnumerableのインタフェースはListの親インタフェースです。
つまり、Listみたいに使えるインタフェースですが、正確にはイテレータパターン(iterator pattern)のインタフェースです。
つまり、Listと似てますが、AddやRemoveみたいにデータを追加、削除をできなく、foreachでデータを取得することしかできないです。
参考でLinqで抽出した結果にデータを追加、削除するためにはToList()関数でListタイプにキャストして使ったら良いです。
using System;
using System.Collections.Generic;
using System.Linq;
namespace Example
{
// 例クラス
class Node
{
// 値をコンストラクタで格納する。
public Node(int data)
{
// プロパティのDataに値を格納
this.Data = data;
}
// Dataプロパティ
public int Data
{
// 入力はコンストラクタから受け取る。
get; private set;
}
}
class Program
{
// 実行関数
static void Main(string[] args)
{
// リスト宣言(リストのオブジェクトはNodeクラス)
var list = new List<Node>();
// iが0から9まで繰り返し
for (int i = 0; i < 10; i++)
{
// リストにデータを挿入
list.Add(new Node(i));
}
// NodeクラスのData値が6以上のインスタンスをList<int>タイプで分類する。
var filterList = from node in list where node.Data > 5 select node.Data;
// 上のクエリ式の結果IEnumerableタイプからListタイプにキャスト
var newList = filterList.ToList();
// select node.DataによりList<int>タイプで設定したので、Nodeインスタンスを入れることではない。
newList.Add(1);
// 新しいリストを繰り返し文で抽出
foreach (var node in newList)
{
// コンソール出力
Console.WriteLine(node);
}
// 任意のキーを押してください
Console.WriteLine("Press any key...");
Console.ReadLine();
}
}
}
また、クエリ式に戻って、実務プログラムを開発するとfrom where selectが一番よく使います。
その以外にorderbyがあるし、join、letをよく使います。
orderbyはデータの整列する式だし、joinは二つのリストを合併する式です。
using System;
using System.Collections.Generic;
using System.Linq;
namespace Example
{
// 例クラス
class Node
{
// 値をコンストラクタで格納する。
public Node(int key, string value)
{
// プロパティに値を格納
this.Key = key;
this.Value = value;
}
// Keyプロパティ
public int Key
{
// 入力はコンストラクタから受け取る。
get; private set;
}
// Valueプロパティ
public string Value
{
// 入力はコンストラクタから受け取る。
get; private set;
}
}
class Program
{
// 実行関数
static void Main(string[] args)
{
// リスト宣言(リストのオブジェクトはNodeクラス)
var list1 = new List<Node>();
var list2 = new List<Node>();
// iが0から9まで繰り返し
for (int i = 0; i < 10; i++)
{
// リストにデータを挿入
list1.Add(new Node(i, "List1:" + i));
list2.Add(new Node(i, "List2:" + i));
}
// list1の基本リストとlist2の6以上の結果値のnode2で置換してKeyでマッピングして結果をnode3に生成する。
// node1のキーで降順で整列して
// 結果をまた、Nodeインスタンスを生成してIEnumerableでリターンする。
// valueの値はnode3でマッピングしない値、つまり0から5までの値は空のstringを格納してその以外はnode3のvalue値を格納する。
var filterList = from node1 in list1
join node2 in (from sub in list2 where sub.Key > 5 select sub) on node1.Key equals node2.Key into node3
orderby node1.Key descending
select new Node(node1.Key, node3.ToArray().Length > 0 ? node3.ToArray()[0].Value : "");
// フィルターされたリストを繰り返しで抽出
foreach (var item in filterList)
{
// コンソール出力
Console.WriteLine("Key - " + item.Key + " Value - " + item.Value);
}
// 任意のキーを押してください
Console.WriteLine("Press any key...");
Console.ReadLine();
}
}
}
Linq式のJoinはデータベースのouter joinと似てます。
つまり、最初のlist1を基準でlist2をjoinして結果をintoのキーワードでnode3に作成します。joinになるデータで値がなければ0個のIEnumerableデータで、あれば1個のIEnumerableデータでnode3に置換します。
そして順番をorderbyに通ってlist1基準の降順に再整列します。
その結果をselectで新しいNodeインスタンスで再生成します。
コンソール結果をみれば降順で出力するし、0から5までのデータはマッピングする値がないので空白stringで出力します。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Example
{
// 例クラス
class Node
{
// 値をコンストラクタで格納する。
public Node(int key, string value)
{
// プロパティに値を格納
this.Key = key;
this.Value = value;
}
// Keyプロパティ
public int Key
{
// 入力はコンストラクタから受け取る。
get; private set;
}
// Valueプロパティ
public string Value
{
// 入力はコンストラクタから受け取る。
get; private set;
}
// クラスのStringをリターン
public override string ToString()
{
return "Key - " + Key + " Value - " + Value;
}
}
// Groupされたクラス
class Group
{
// 値をコンストラクタで格納する。
public Group(int key, IEnumerable<Node> value)
{
// プロパティに値を格納
this.Key = key;
this.Value = value;
}
// Keyプロパティ
public int Key
{
// 入力はコンストラクタから受け取る。
get; private set;
}
// Valueプロパティ
public IEnumerable<Node> Value
{
// 入力はコンストラクタから受け取る。
get; private set;
}
// クラスのStringをリターン
public override string ToString()
{
// リストになっているvalueデータのString値を作成
var sb = new StringBuilder();
foreach (var item in Value)
{
sb.AppendLine(item.ToString());
}
return sb.ToString();
}
}
class Program
{
// 実行関数
static void Main(string[] args)
{
// リスト宣言(リストのオブジェクトはNodeクラス)
var list = new List<Node>();
// iが0から9まで繰り返し
for (int i = 0; i < 10; i++)
{
// リストにデータを挿入
list.Add(new Node(i, "List:" + i));
}
// 偶数、奇数別でグループを分けてそのキーでNodeを再整列する。
var filerList = from node in list group node by node.Key % 2 into g select new Group(g.Key, g);
// filerListのキー順番とおりに繰り返し
foreach (var item in filerList)
{
// コンソール出力
Console.WriteLine("Group Key - " + item.Key);
Console.WriteLine(item.ToString());
}
// 任意のキーを押してください
Console.WriteLine("Press any key...");
Console.ReadLine();
}
}
}
上の例はリストをグループ化しました。
まず、groupはグループする対象を設定してbyによりキーを設定します。node.Key % 2ですべてリストに出られる場合の数は、0と1です。
つまり、0と1のリストを作って0になる値のlistと1になる値のリストを作りました。その結果をGroupというクラスのインスタンスを生成してまた、0と1のIEnumerableタイプで生成します。
結果をみれば0と1でグループになることを確認できます。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Example
{
// 例クラス
class Node
{
// 値をコンストラクタで格納する。
public Node(int data)
{
// プロパティのDataに値を格納
this.Data = data;
}
// Dataプロパティ
public int Data
{
// 入力はコンストラクタから受け取る。
get; private set;
}
}
class Program
{
// 実行関数
static void Main(string[] args)
{
// リスト宣言(リストのオブジェクトはNodeクラス)
var list = new List<Node>();
// iが0から9まで繰り返し
for (int i = 0; i < 10; i++)
{
// リストにデータを挿入
list.Add(new Node(i));
}
// letは変数の再定義
var filerList = from node in list let data = node.Data * 100 select new Node(data);
// filerListのキー順番とおりに繰り返し
foreach (var item in filerList)
{
// コンソール出力
Console.WriteLine(item.Data);
}
// 任意のキーを押してください
Console.WriteLine("Press any key...");
Console.ReadLine();
}
}
}
上のクエリ式ではletを使いました。
letのキーワードはデータを再定義する式です。つまり、from node in listで再定義したnodeのNodeクラスのインスタンスをletによりnode.Dataの値に100を掛けてint値で再置換します。
それをまた、selectに通ってNodeクラスのインスタンスを生成してリストで作ります。
結果はnode.Dataが100に掛けて、Nodeのインスタンスリストで結果が出ることを確認できます。
Linqのクエリ式はMSの.net framework Docで説明しているので、参照したら良いです。
link - https://docs.microsoft.com
Linqクエリ式をよく使ったら、プログラムを作成する時、ソースのステップを減らすこともできるし、検証されたクエリ式なのでユーザが作成したアルゴリズムより良い性能で使えます。
もちろん、それがクエリをしっかり使う場合です。不必要なjoinとfrom、letによりデータ合併、置換、そして間違いwhereが逆に性能が悪くなります。
そしてまた、クエリ式は普通のプログラムコード式ではないので、近すぎなら、ソース読みにくい(可読性低下)し、リストのフィルター、Join,置換状態のデバッグを確認できないので、開発するところで不便なことが多いです。
そして、逆に実務開発環境ではLinqのクエリ式より関数式をよく使います。
ここまでC#のLinqクエリ式を使い方に関する説明でした。
ご不明なところや間違いところがあればコメントしてください。
- [C#] 36. 拡張メソッドを使い方2019/07/22 23:30:17
- [C#] 35. 文字列クラス、StringとStringBuilderを使い方2019/07/22 23:15:42
- [C#] 34. 最上位クラス(Object クラス)2019/07/20 02:27:23
- [C#] 33. 匿名形式(Anonymous Types)を使い方2019/07/20 02:22:03
- [C#] 32. ジェネリックタイプ(Generic Type)を使い方2019/07/18 22:50:16
- [C#] 31. アトリビュート(Attribute)を使い方2019/07/18 20:22:16
- [C#] 30. Linq関数式を使う方法2019/07/17 23:06:42
- [C#] 29. Linqクエリ式を使い方2019/07/17 20:57:00
- [C#] 28. リスト(List)とディクショナリ(Dictionary)、そしてLinq式を使い方2019/07/16 22:40:03
- [C#] 27. varキーワードとdynamicキーワード2019/07/16 20:41:27
- [C#] 26. 例外処理(try ~ catch)する方法2019/07/16 00:59:34
- [C#] 25. イベント(event)キーワードを使い方2019/07/16 00:48:03
- [C#] 24. ラムダ式(匿名関数)とAction、Func関数を使い方、そしてクロージャ(Closure)2019/07/16 00:36:19
- [C#] 23. デリゲート(delegate)2019/07/15 02:25:26
- [C#] 22. インデクサー(indexer)を使う方法2019/07/13 01:06:04
- check2024/04/10 19:03:53
- [Java] 64.Spring bootとReactを連結する方法(Buildする方法)2022/03/25 21:02:18
- [Javascript] Node.jsをインストールしてReactを使う方法2022/03/23 18:01:34
- [Java] 63. Spring bootでcronスケジューラとComponentアノテーション2022/03/16 18:57:30
- [Java] 62. Spring bootでWeb-Filterを設定する方法(Spring Security)2022/03/15 22:16:37
- [Java] JWT(Json Web Token)を発行、確認する方法2022/03/14 19:12:58
- [Java] 61. Spring bootでRedisデータベースを利用してセッションクラスタリング設定する方法2022/03/01 18:20:52
- [Java] 60. Spring bootでApacheの連結とロードバランシングを設定する方法2022/02/28 18:45:48
- [Java] 59. Spring bootのJPAでEntityManagerを使い方2022/02/25 18:27:48
- [Java] 58. EclipseでSpring bootのJPAを設定する方法2022/02/23 18:11:10
- [Java] 57. EclipseでSpring bootを設定する方法2022/02/22 19:04:49
- [Python] Redisデータベースに接続して使い方2022/02/21 18:23:49
- [Java] Redisデータベースを接続して使い方(Jedisライブラリ)2022/02/16 18:13:17
- [C#] Redisのデータベースを接続して使い方2022/02/15 18:46:09
- [CentOS] Redisデータベースをインストールする方法とコマンドを使い方2022/02/14 18:33:07