「Linq 動的 Where」などってググってみると、いろいろ情報が出てくると思います。
Expressionで動的に組み立てるのが正攻法なようですね。
う~んメンドクサイ。
メンドクサイ事はイヤなので簡単に動的OR検索する方法です。
テストデータです。
【C#】
private class Fruit { public string Name { get; set; } public string Rank { get; set; } public decimal Price { get; set; } } var fruits = new List<Fruit>() { new Fruit(){Name = "りんご", Rank = "A" , Price = 1000 }, new Fruit(){Name = "みかん", Rank = "A" , Price = 600 }, new Fruit(){Name = "ぶどう", Rank = "B" , Price = 1200 }, new Fruit(){Name = "りんご", Rank = "B" , Price = 800 }, new Fruit(){Name = "みかん", Rank = "A" , Price = 500 }, new Fruit(){Name = "ばなな", Rank = "C" , Price = 100 } };【VB】
Private Class Fruit Public Property Name As String Public Property Rank As String Public Property Price As Decimal End Class Dim fruits = New List(Of Fruit)() From { New Fruit() With {.Name = "りんご", .Rank = "A", .Price = 1000}, New Fruit() With {.Name = "みかん", .Rank = "A", .Price = 600}, New Fruit() With {.Name = "ぶどう", .Rank = "B", .Price = 1200}, New Fruit() With {.Name = "りんご", .Rank = "B", .Price = 800}, New Fruit() With {.Name = "みかん", .Rank = "A", .Price = 500}, New Fruit() With {.Name = "ばなな", .Rank = "C", .Price = 500} }
検索画面から検索条件が指定されたとします。
名称は複数指定でき、条件値はリストになっています。今回の条件は「りんご または みかん」
ランクも複数指定で、条件値はリストになっています。今回の条件は指定がありません。
値段は下限値と上限値が指定できます。今回の条件は800円以下です。
つまり、今回の条件は「りんご か みかん」 か 「800円以下」の果物になります。
【C#】
//検索条件 //--名称 var searchNameList = new List<string>() { "りんご", "みかん" }; //--ランク var searchRankList = new List<string>() { /*指定なし*/ }; //--値段(下限、上限) var priceRange = new Tuple<decimal?, decimal?>(null, 800);
【VB】
'検索条件 '--名称 Dim searchNameList = New List(Of String)() From {"りんご", "みかん"} '--ランク Dim searchRankList = New List(Of String)() From {} '指定なし '--値段(下限、上限) Dim priceRange = New Tuple(Of Decimal?, Decimal?)(Nothing, 800)
次に検索条件が指定されている場合に実行する処理を、リストに溜めて行きます。
List型の変数「funcSearchList」には、Fruit型の引数をとりboolを返す処理を格納できます。
例えば名称で検索する場合、Fruit型の引数が名称検索条件内に存在すればtrueを返す処理を、リストに追加しています。
【C#】
//検索処理リストに検索条件ごとの処理を追加していく var funcSeachList = new List<Func<Fruit, bool>>(); //--名称 if (searchNameList.Count > 0) { funcSeachList.Add((fruit) => { return searchNameList.Contains(fruit.Name); }); } //--ランク if (searchRankList.Count > 0) { funcSeachList.Add((fruit) => { return searchRankList.Contains(fruit.Rank); }); } //--値段 if (priceRange.Item1.HasValue) { funcSeachList.Add((fruit) => { return fruit.Price >= priceRange.Item1.Value; }); } if (priceRange.Item2.HasValue) { funcSeachList.Add((fruit) => { return fruit.Price <= priceRange.Item2.Value; }); }【VB】
'検索処理リストに検索条件ごとの処理を追加していく Dim funcSeachList = New List(Of Func(Of Fruit, Boolean))() '--名称 If (searchNameList.Count > 0) Then funcSeachList.Add(Function(fruit) searchNameList.Contains(fruit.Name)) End If '--ランク If (searchRankList.Count > 0) Then funcSeachList.Add(Function(fruit) searchRankList.Contains(fruit.Rank)) End If '--値段 If (priceRange.Item1.HasValue) Then funcSeachList.Add(Function(fruit) fruit.Price >= priceRange.Item1.Value) End If If (priceRange.Item2.HasValue) Then funcSeachList.Add(Function(fruit) fruit.Price <= priceRange.Item2.Value) End If
検索処理を溜めたList型の変数「funcSearchList」から、OR検索用のメソッドを作成します。
【C#】
//--Or検索用メソッドの作成 Func<Fruit, bool> searchOr = (fruit) => { //--検索条件が1件もなければ、要素は条件に一致したとする。 if (funcSeachList.Count == 0) return true; //--要素が検索条件に一致するか判定する bool? result = null; foreach (var func in funcSeachList) { bool funcResult = func(fruit); //And検索したければココを||から&&に変更する result = (result.HasValue ? result.Value || funcResult : funcResult); } return result.Value; };【VB】
'--Or検索用メソッドの作成 Dim searchOr = Function(fruit As Fruit) '--検索条件が1件もなければ、要素は条件に一致したとする。 If (funcSeachList.Count = 0) Then Return True End If '--要素が検索条件に一致するか判定する Dim result As Nullable(Of Boolean) = Nothing For Each func In funcSeachList Dim funcResult As Boolean = func(fruit) 'And検索したければココをOrElseからAndAlsoに変更する result = If(result.HasValue, result.Value OrElse funcResult, funcResult) Next Return result.Value End Functionあとは作成したメソッドをWhereメソッドの引数に指定してあげればOKです。
【C#】
//Or検索 var searchOrResultList = fruits.Where(itm => { return searchOr(itm); }); //出力 foreach (var itm in searchOrResultList) Console.WriteLine("名称={0}, ランク={1}, 値段={2}", itm.Name, itm.Rank, itm.Price.ToString("#,##0")); //名称=りんご, ランク=A, 値段=1,000 //名称=みかん, ランク=A, 値段=600 //名称=りんご, ランク=B, 値段=800 //名称=みかん, ランク=A, 値段=500 //名称=ばなな, ランク=C, 値段=100【VB】
'Or検索 Dim searchOrResultList = fruits.Where(Function(itm) searchOr(itm)) '出力 For Each itm In searchOrResultList Console.WriteLine("名称={0}, ランク={1}, 値段={2}", itm.Name, itm.Rank, itm.Price.ToString("#,##0")) Next '名称=りんご, ランク=A, 値段=1,000 '名称=みかん, ランク=A, 値段=600 '名称=りんご, ランク=B, 値段=800 '名称=みかん, ランク=A, 値段=500 '名称=ばなな, ランク=C, 値段=100
コード全体です。
【C#】
//テストデータ作成 var fruits = new List<Fruit>() { new Fruit(){Name = "りんご", Rank = "A" , Price = 1000 }, new Fruit(){Name = "みかん", Rank = "A" , Price = 600 }, new Fruit(){Name = "ぶどう", Rank = "B" , Price = 1200 }, new Fruit(){Name = "りんご", Rank = "B" , Price = 800 }, new Fruit(){Name = "みかん", Rank = "A" , Price = 500 }, new Fruit(){Name = "ばなな", Rank = "C" , Price = 100 } }; /* 検索画面から検索条件が指定されたとする */ //検索条件 //--名称 var searchNameList = new List<string>() { "りんご", "みかん" }; //--ランク var searchRankList = new List<string>() { /*指定なし*/ }; //--値段(下限、上限) var priceRange = new Tuple<decimal?, decimal?>(null, 800); //検索処理リストに検索条件ごとの処理を追加していく var funcSeachList = new List<Func<Fruit, bool>>(); //--名称 if (searchNameList.Count > 0) { funcSeachList.Add((fruit) => { return searchNameList.Contains(fruit.Name); }); } //--ランク if (searchRankList.Count > 0) { funcSeachList.Add((fruit) => { return searchRankList.Contains(fruit.Rank); }); } //--値段 if (priceRange.Item1.HasValue) { funcSeachList.Add((fruit) => { return fruit.Price >= priceRange.Item1.Value; }); } if (priceRange.Item2.HasValue) { funcSeachList.Add((fruit) => { return fruit.Price <= priceRange.Item2.Value; }); } //Or検索用メソッドの作成 Func<Fruit, bool> searchOr = (fruit) => { //--検索条件が1件もなければ、要素は条件に一致したとする。 if (funcSeachList.Count == 0) return true; //--要素が検索条件に一致するか判定する bool? result = null; foreach (var func in funcSeachList) { bool funcResult = func(fruit); result = (result.HasValue ? result.Value || funcResult : funcResult); } return result.Value; }; //Or検索 var searchOrResultList = fruits.Where(itm => { return searchOr(itm); }); //出力 foreach (var itm in searchOrResultList) Console.WriteLine("名称={0}, ランク={1}, 値段={2}", itm.Name, itm.Rank, itm.Price.ToString("#,##0"));【VB】
'テストデータ作成 Dim fruits = New List(Of Fruit)() From { New Fruit() With {.Name = "りんご", .Rank = "A", .Price = 1000}, New Fruit() With {.Name = "みかん", .Rank = "A", .Price = 600}, New Fruit() With {.Name = "ぶどう", .Rank = "B", .Price = 1200}, New Fruit() With {.Name = "りんご", .Rank = "B", .Price = 800}, New Fruit() With {.Name = "みかん", .Rank = "A", .Price = 500}, New Fruit() With {.Name = "ばなな", .Rank = "C", .Price = 500} } '検索画面から検索条件が指定されたとする '検索条件 '--名称 Dim searchNameList = New List(Of String)() From {"りんご", "みかん"} '--ランク Dim searchRankList = New List(Of String)() From {} '指定なし '--値段(下限、上限) Dim priceRange = New Tuple(Of Decimal?, Decimal?)(Nothing, 800) '検索処理リストに検索条件ごとの処理を追加していく Dim funcSeachList = New List(Of Func(Of Fruit, Boolean))() '--名称 If (searchNameList.Count > 0) Then funcSeachList.Add(Function(fruit) searchNameList.Contains(fruit.Name)) End If '--ランク If (searchRankList.Count > 0) Then funcSeachList.Add(Function(fruit) searchRankList.Contains(fruit.Rank)) End If '--値段 If (priceRange.Item1.HasValue) Then funcSeachList.Add(Function(fruit) fruit.Price >= priceRange.Item1.Value) End If If (priceRange.Item2.HasValue) Then funcSeachList.Add(Function(fruit) fruit.Price <= priceRange.Item2.Value) End If 'Or検索用メソッドの作成 Dim searchOr = Function(fruit As Fruit) '--検索条件が1件もなければ、要素は条件に一致したとする。 If (funcSeachList.Count = 0) Then Return True End If '--要素が検索条件に一致するか判定する Dim result As Nullable(Of Boolean) = Nothing For Each func In funcSeachList Dim funcResult As Boolean = func(fruit) result = If(result.HasValue, result.Value OrElse funcResult, funcResult) Next Return result.Value End Function 'Or検索 Dim searchOrResultList = fruits.Where(Function(itm) searchOr(itm)) '出力 For Each itm In searchOrResultList Console.WriteLine("名称={0}, ランク={1}, 値段={2}", itm.Name, itm.Rank, itm.Price.ToString("#,##0")) Next
Or検索用メソッドsearchOrのOR演算子「|| または OrElse」をAND演算子「&& または AndAlso」にかえればAND検索になります。
検索処理を溜めたList型の変数「funcSearchList」に、検索メソッドと一緒にAND検索かOR検索か判定するフラグも格納すればAND検索とOR検索のゴチャ混ぜ検索もできます。
OR検索用メソッドを組み立てる部分は汎用化できるので、Linqに独自の拡張メソッドとして追加してもいいかと思います。
以下はOR検索する処理のリストを引数にとり、条件に該当した要素のリストを返す拡張メソッドです。
【C#】
public static class LinqExtension { public static IEnumerable<T> WhereOr<T>(this IEnumerable<T> source, List<Func<T, bool>> lstFunc) { //検索条件が1件もなければ、要素は条件に一致したとする。 if (lstFunc.Count == 0) return source; //検索条件に一致した要素のリストを作成する var returnList = new List<T>(); foreach (var item in source) { //--要素が検索条件に一致するか判定する bool? match = null; foreach (var func in lstFunc) { bool funcResult = func(item); match = (match.HasValue ? match.Value || funcResult : funcResult); } //--条件が一致した要素のみリストに追加 if (match.Value == true) returnList.Add(item); } return returnList; } }【VB】
Imports System.Runtime.CompilerServices Module LinqExtenstions <Extension()> _ Public Function WhereOr(Of T) _ (ByVal source As IEnumerable(Of T), lstFunc As List(Of Func(Of T, Boolean))) As IEnumerable(Of T) '検索条件が1件もなければ、要素は条件に一致したとする。 If (lstFunc.Count = 0) Then Return source End If '検索条件に一致した要素のリストを作成する Dim returnList = New List(Of T)() For Each item In source '--要素が検索条件に一致するか判定する Dim match As Nullable(Of Boolean) = Nothing For Each func In lstFunc Dim funcResult As Boolean = func(item) match = If(match.HasValue, match.Value OrElse funcResult, funcResult) Next '--条件が一致した要素のみリストに追加 If (match.Value = True) Then returnList.Add(item) End If Next Return returnList End Function End Module
使用方法は、先ほどのWhereメソッド部分をWhereOrメソッドに変更します。
【C#】
//テストデータ作成 var fruits = new List<Fruit>() { new Fruit(){Name = "りんご", Rank = "A" , Price = 1000 }, new Fruit(){Name = "みかん", Rank = "A" , Price = 600 }, new Fruit(){Name = "ぶどう", Rank = "B" , Price = 1200 }, new Fruit(){Name = "りんご", Rank = "B" , Price = 800 }, new Fruit(){Name = "みかん", Rank = "A" , Price = 500 }, new Fruit(){Name = "ばなな", Rank = "C" , Price = 100 } }; /* 検索画面から検索条件が指定されたとする */ //検索条件 //--名称 var searchNameList = new List<string>() { "りんご", "みかん" }; //--ランク var searchRankList = new List<string>() { /*指定なし*/ }; //--値段(下限、上限) var priceRange = new Tuple<decimal?, decimal?>(null, 800); //検索処理リストに検索条件ごとの処理を追加していく var funcSeachList = new List<Func<Fruit, bool>>(); //--名称 if (searchNameList.Count > 0) { funcSeachList.Add((fruit) => { return searchNameList.Contains(fruit.Name); }); } //--ランク if (searchRankList.Count > 0) { funcSeachList.Add((fruit) => { return searchRankList.Contains(fruit.Rank); }); } //--値段 if (priceRange.Item1.HasValue) { funcSeachList.Add((fruit) => { return fruit.Price >= priceRange.Item1.Value; }); } if (priceRange.Item2.HasValue) { funcSeachList.Add((fruit) => { return fruit.Price <= priceRange.Item2.Value; }); } //Or検索 //独自に追加したWhereOrメソッドを使用する var searchOrResultList = fruits.WhereOr(funcSeachList); //出力 foreach (var itm in searchOrResultList) Console.WriteLine("名称={0}, ランク={1}, 値段={2}", itm.Name, itm.Rank, itm.Price.ToString("#,##0"));【VB】
'テストデータ作成 Dim fruits = New List(Of Fruit)() From { New Fruit() With {.Name = "りんご", .Rank = "A", .Price = 1000}, New Fruit() With {.Name = "みかん", .Rank = "A", .Price = 600}, New Fruit() With {.Name = "ぶどう", .Rank = "B", .Price = 1200}, New Fruit() With {.Name = "りんご", .Rank = "B", .Price = 800}, New Fruit() With {.Name = "みかん", .Rank = "A", .Price = 500}, New Fruit() With {.Name = "ばなな", .Rank = "C", .Price = 500} } '検索画面から検索条件が指定されたとする '検索条件 '--名称 Dim searchNameList = New List(Of String)() From {"りんご", "みかん"} '--ランク Dim searchRankList = New List(Of String)() From {} '指定なし '--値段(下限、上限) Dim priceRange = New Tuple(Of Decimal?, Decimal?)(Nothing, 800) '検索処理リストに検索条件ごとの処理を追加していく Dim funcSeachList = New List(Of Func(Of Fruit, Boolean))() '--名称 If (searchNameList.Count > 0) Then funcSeachList.Add(Function(fruit) searchNameList.Contains(fruit.Name)) End If '--ランク If (searchRankList.Count > 0) Then funcSeachList.Add(Function(fruit) searchRankList.Contains(fruit.Rank)) End If '--値段 If (priceRange.Item1.HasValue) Then funcSeachList.Add(Function(fruit) fruit.Price >= priceRange.Item1.Value) End If If (priceRange.Item2.HasValue) Then funcSeachList.Add(Function(fruit) fruit.Price <= priceRange.Item2.Value) End If 'Or検索 '独自に追加したWhereOrメソッドを使用する Dim searchOrResultList = fruits.WhereOr(funcSeachList) '出力 For Each itm In searchOrResultList Console.WriteLine("名称={0}, ランク={1}, 値段={2}", itm.Name, itm.Rank, itm.Price.ToString("#,##0")) Next
.Net(VB C#) LINQのメソッド一覧
0 件のコメント:
コメントを投稿