画面の検索条件などによりWhereメソッドの条件を動的に組み立てる事があると思います。
「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 件のコメント:
コメントを投稿