こんにちは。明月です。
この投稿はC#のLinq関数式を使う方法に関する説明です。
以前の投稿でLinq式に関して簡単な説明とLinqクエリ式に関して説明したことがあります。
link - [C#] 28. リスト(List)とディクショナリ(Dictionary)、そしてLinq式を使い方
link - [C#] 29. Linqクエリ式を使い方
Linq式に関してはプログラム内のオブジェクト(Object)を効果的にフィルター、建さんしてデータを分類する文法です。
クエリ式の場合はSQLクエリみたいに作成してデータを分類する方法です。
クエリ式の利点はSQLクエリ式に慣れている方にはこの文法が慣れしやすいですが、少しプログラム文法と違和感があるし、途中でデバッグ状況を確認することが難しいので使うことでお勧めではありません。
それでLinq式にはプログラム関数と同じLinq関数式があります。
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 = list.Where(x => x.Data > 5).Select(x => x.Data);
// フィルターされたリストを繰り返しで抽出
foreach (var node in filterList)
{
// コンソール出力
Console.WriteLine(node);
}
// 任意のキーを押してください
Console.WriteLine("Press any key...");
Console.ReadLine();
}
}
}
上のソースではクエリ式のfrom node in list where node.Data > 5 select node.Data;をlist.Where(x => x.Data > 5).Select(x => x.Data);で作成しました。
ここでWhere関数とSelect関数の中ではラムダ式を使ってフィルターと検索条件を生成しました。
そしてWhere関数の返却値とSelect関数の返却値はIEnumerableタイプなので、関数を連結するみたいにチェインパターンで生成することができます。
つまり、上みたいにチェインパターンではなく、一行目ずつ作成することもできます。(参考、Selectの場合はReturn値によりジェネリックタイプが変わるので注意)
基本的にLinq関数式の場合はWhereとSelectをよく使いますが、仕様により、Joinと整列、グループ別に分離することもできます。
First, FirstOrDefault, Single, SingleOrDefault, Last, LastOrDefault
この関数はリストの結果を一つの結果に受け取る場合に使います。最終の返却値はListではなく、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));
}
// listで6以上のデータを分類
var where = list.Where(x => x.Data > 5);
// listで一番先のデータを抽出
Node first = where.First();
// listで一番後のデータを抽出
Node last = where.Last();
// コンソール出力
Console.WriteLine("First - " + first.Data);
Console.WriteLine("Last - " + last.Data);
// 上の6以上の検索節から7と一致するデータを分類
where = where.Where(x => x.Data == 7);
// listのデータが一つなら抽出(2個以上ならException)
Node single = where.Single();
// コンソール出力
Console.WriteLine("Single - " + single.Data);
// 上の6以上の検索節かつ7と一致するデータから10と一致するデータを分類(必ず分類されるデータはなし)
where = where.Where(x => x.Data == 10);
// listからデータが一つなら抽出、なければnullをリターン
Node default1 = where.SingleOrDefault();
// コンソール出力
Console.WriteLine("Default - " + (default1 == null ? "Null" : "Not null"));
// 任意のキーを押してください
Console.WriteLine("Press any key...");
Console.ReadLine();
}
}
}
First、Lastの場合はリストから始めの値と最後のの値を返却する関数式です。でもデータがなければExceptionが発生します。
でも、ここでorDefaultが付けると、つまり、FirstOrDefault、LastOrDefaultなら検索されたデータがなければnullをリターンします。エラーが発生しません。
Singleの関数はリストの結果が一つの場合、リターンする関数式です。もし、結果値が二つ以上ならエラーを発生します。
SingleOrDefaultの関数も結果がなければnullをリターンします。
OrderBy, OrderByDescending
データを整列する関数です。
OrderByの場合は昇順、OrderByDescendingの場合は降順になります。
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));
}
// listで6以上のデータを分類
var where = list.Where(x => x.Data > 5);
// 降順に整列
where = where.OrderByDescending(x => x.Data);
// フィルターされたリストを繰り返しで抽出
foreach (var node in filterList)
{
// コンソール出力
Console.WriteLine(node);
}
// 任意のキーを押してください
Console.WriteLine("Press any key...");
Console.ReadLine();
}
}
}
Select, SelectMany
Selectはデータを置換することだし、SelectManyは二つのリストを合併することです。
ここで合併するのはリストのJoinをする意味ではなく、Aのリストが二つあり、Bのリストが三つがあれば、総6個のリストを作るという意味です。
using System;
using System.Collections.Generic;
using System.Linq;
namespace Example
{
class Program
{
// 実行関数
static void Main(string[] args)
{
// リスト宣言
var list1 = new List<String>();
var list2 = new List<int>();
// list1は文字タイプ
list1.Add("1");
list1.Add("2");
// list2は整数タイプ
list2.Add(3);
list2.Add(4);
list2.Add(5);
// list1をSelectにより整数タイプに置換してSelectManyにより二つのリストを合併します。
var ret = list1.Select(x => Convert.ToInt32(x)).SelectMany(x => list2, (x, y) => x + " * " + y + " = " + (x * y));
foreach (var i in ret)
{
// コンソール出力
Console.WriteLine(i);
}
// 任意のキーを押してください
Console.WriteLine("Press any key...");
Console.ReadLine();
}
}
}
Join
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));
}
// Join関数のパラメータは合併するリスト、list1に関する比較キー、list2に関する比較キー、Joinされた結果リターン値
var ret = list1.Join(list2.Where(x => x.Key > 5), x => x.Key, y => y.Key, (x, y) => x.Value + " " + y.Value);
// フィルターされたリストを繰り返しで抽出
foreach (var item in filterList)
{
// コンソール出力
Console.WriteLine("Key - " + item.Key + " Value - " + item.Value);
}
// 任意のキーを押してください
Console.WriteLine("Press any key...");
Console.ReadLine();
}
}
}
Group
リストをGroup別に分ける関数です。
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 = list.GroupBy(x => x.Key % 2, x => x).Select(x => new Group(x.Key, x.ToList()));
// filerListのキー順番とおりに繰り返し
foreach (var item in filerList)
{
// コンソール出力
Console.WriteLine("Group Key - " + item.Key);
Console.WriteLine(item.ToString());
}
// 任意のキーを押してください
Console.WriteLine("Press any key...");
Console.ReadLine();
}
}
}
Sum, Average, Max, Min
Linq関数式にはクエリ式にはない関数がありますが、それがSum、Average、Max、Minです。Sumはリストの結果合、Averageは平均、Maxは一番大きい値、Minは小さい値の結果になります。
using System;
using System.Collections.Generic;
using System.Linq;
namespace Example
{
class Program
{
// 実行関数
static void Main(string[] args)
{
// リスト宣言
var list = new List<int>();
// iが0から9まで繰り返し
for (int i = 0; i < 10; i++)
{
// リストにデータを挿入
list.Add(i);
}
// リストの中で一番大きい値を救う。
var max = list.Max(x => x);
// リストの中で一番小さい値を救う。
var min = list.Min(x => x);
// リストの合を救う。
var sum = list.Sum(x => x);
// リストの平均値を救う。
var avg = list.Average(x => x);
// コンソール出力
Console.WriteLine(max);
Console.WriteLine(min);
Console.WriteLine(sum);
Console.WriteLine(avg);
// 任意のキーを押してください
Console.WriteLine("Press any key...");
Console.ReadLine();
}
}
}
Linq関数式はクエリ式よりソース読みやすさが良いです。
そしてすべての関数式はIEnumerableインタフェースでリターンするので、チェインパターン式で一行目で様々な関数を呼び出すことができます。
C#の代表的なORMのEntityプレームワークもLinqの関数式を継承して使うので様々な仕様でも簡単に使うことができます。
でも、Linq式とは関数自体のアルゴリズムは凄く効率的ですが、使用方法や仕様により性能を低下する可能性があるので、注意しなければならないです。
例えば、重複フィルターや一つの条件式で処理することをいろんなWhereとSelectを分けるのはよくないです。
ここまでC#のLinq関数式を使う方法に関する説明でした。
ご不明なところや間違いところがあればコメントしてください。
- [C#] 37. スレッド(Thread)を使い方、Thread.Sleep関数を使い方2019/07/22 23:45:05
- [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
- 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