如何将 LINQ 用于循环内的多个过滤器?
How to use LINQ for multiple filters inside a loop?
我正在使用 linq 来过滤我的数据,即如果数据是数字类型,我必须使用比较器,否则我需要字符串匹配。
即使我的 linq 查询在循环内,我目前也只能使用一个过滤器。
List<(string col, string opr, string value)> ps = new List<(string col, string opr, string value)>();
Func<ShoeModels, string, string, float, bool> comparer = (ShoeModels a, string column, string op, float value) =>
{
switch (op)
{
case "lt": return a[column] < value;
case "gt": return a[column] > value;
case "gte": return a[column] >= value;
case "lte": return a[column] <= value;
case "ne": return a[column] != value;
default:
break;
}
return true;
};
for(i=5;i<query.count;i++)
{
if (ps[i].value.All(char.IsDigit))
{
query.numValue = float.Parse(ps[i].value);
}
else
{
query.filterValue = ps[i].value;
}
query.filterColumn = ps[i].col;
query.opr = ps[i].opr;
query.opr = query.opr.ToLower();
IEnumerable<ShoeModels> res = null;
if (query.opr == "gt" || query.opr == "lt" || query.opr == "gte" || query.opr == "lte" || query.opr == "ne")
{
IEnumerable<ShoeModels> tuyo = from p in loopData.Where(a => comparer(a, query.filterColumn, query.opr, query.numValue))
select p;
res = tuyo.ToList();
}
else if (query.opr.ToLower() == "like")
{
IEnumerable<ShoeModels> tuyo = from p in loopData.Where(a => a[query.filterColumn].Contains(query.filterValue))
select p;
res = tuyo.ToList();
}
finalResult = res.ToList();
}
正如您在代码中看到的那样,我使用 LHS 方括号运算符来过滤我的数据。现在,如果我在比较之前使用我的 like 运算符,我会得到一个异常 "float does not contain definition for Contains"。
所以我通过阅读延迟执行然后在每次执行后将 ToList() 添加到结果来解决这个问题。所以现在有一个问题,每当我尝试过滤代码时,只有最后一个过滤器有效,但所有过滤器都应该有效。
您只有最后一个过滤器起作用,因为您在 for
循环的每次迭代中将过滤器应用于 loopData
:
from p in loopData.Where()...
因此,每次您过滤初始 loopData
而不是上次过滤产生的数据。
您需要将每次迭代的初步过滤结果保存在一些其他变量中,这些变量应该在 for
循环之外声明(在我的代码示例中,这个变量是 res
变量)并应用每次迭代中该 res
变量的其他过滤器。
List<ShoeModels> res = loopData; // here I declare and assign res variable, it should be outside of the loop
for (int i = 5; i < query.count; i++)
{
if (ps[i].value.All(char.IsDigit))
{
query.numValue = float.Parse(ps[i].value);
}
else
{
query.filterValue = ps[i].value;
}
query.filterColumn = ps[i].col;
query.opr = ps[i].opr;
query.opr = query.opr.ToLower();
IEnumerable<ShoeModels> tuyo = null;
if (query.opr == "gt" || query.opr == "lt" || query.opr == "gte" || query.opr == "lte" || query.opr == "ne")
{
// apply filtering to the res variable
tuyo = res.Where(a => comparer(a, query.filterColumn, query.opr, query.numValue));
}
else if (query.opr.ToLower() == "like")
{
// apply filtering to the res variable
tuyo = res.Where(a => a[query.filterColumn].Contains(query.filterValue));
}
// update res variable, so on the next iteration we will operate on updated (filtered) list
res = tuyo.ToList();
}
使用 float
来表达价格或金钱是一种不好的做法。使用 decimal
类型。
我在 LinqPad5 中创建了一个演示(如果您不了解 LinqPad,请安装并使用它 - 它非常有用,尤其是对于编写 LINQ 查询):
void Main()
{
var ps = new List<(string col, string opr, string value)>();
ps.Add(("0", "gt", "6"));
ps.Add(("1", "like", "30.0"));
ps.Add(("2", "ne", "60"));
var loopData = new List<ShoeModels> {
new ShoeModels { Range = new[] { 5.0m, 10.0m, 15.0m }}, // leaves out on first filter ps[0]
new ShoeModels { Range = new[] { 10.0m, 20.0m, 30.0m }},// leaves out on second filter ps[1]
new ShoeModels { Range = new[] { 10.0m, 30.0m, 30.0m }},// this is the final result of all three filters
new ShoeModels { Range = new[] { 15.0m, 30.0m, 60.0m }},// leaves out on third filter ps[2]
};
loopData.Dump("initial data");
List<ShoeModels> res = loopData;
var query = new Query();
for (int i = 0; i < ps.Count; i++)
{
decimal number;
if (Decimal.TryParse(ps[i].value, out number))
{
query.numValue = number;
}
else
{
query.filterValue = ps[i].value;
}
query.filterColumn = ps[i].col;
query.opr = ps[i].opr.ToLower();
IEnumerable<ShoeModels> tuyo = null;
if (query.opr == "gt" || query.opr == "lt" || query.opr == "gte" || query.opr == "lte" || query.opr == "ne")
{
tuyo = res.Where(a => comparer(a, query.filterColumn, query.opr, query.numValue));
}
else if (query.opr.ToLower() == "like")
{
tuyo = res.Where(a => (a[query.filterColumn]).ToString(CultureInfo.InvariantCulture).Contains(query.filterValue));
}
res = tuyo.ToList();
res.Dump("after " + i + " iteration");
}
res.Dump("final result");
}
private Func<ShoeModels, string, string, decimal, bool> comparer = (ShoeModels a, string column, string op, decimal value) =>
{
switch (op)
{
case "lt": return a[column] < value;
case "gt": return a[column] > value;
case "gte": return a[column] >= value;
case "lte": return a[column] <= value;
case "ne": return a[column] != value;
default:
break;
}
return true;
};
class Query {
public decimal numValue { get; set;}
public string filterValue { get; set;}
public string filterColumn {get; set;}
public string opr {get; set;}
}
class ShoeModels {
public decimal[] Range = new decimal[3];
public decimal this[string index] {
get => Range[int.Parse(index)];
}
public override string ToString() =>
$"{Range[0].ToString(CultureInfo.InvariantCulture)} " +
$"{Range[1].ToString(CultureInfo.InvariantCulture)} " +
$"{Range[2].ToString(CultureInfo.InvariantCulture)}";
}
我正在使用 linq 来过滤我的数据,即如果数据是数字类型,我必须使用比较器,否则我需要字符串匹配。
即使我的 linq 查询在循环内,我目前也只能使用一个过滤器。
List<(string col, string opr, string value)> ps = new List<(string col, string opr, string value)>();
Func<ShoeModels, string, string, float, bool> comparer = (ShoeModels a, string column, string op, float value) =>
{
switch (op)
{
case "lt": return a[column] < value;
case "gt": return a[column] > value;
case "gte": return a[column] >= value;
case "lte": return a[column] <= value;
case "ne": return a[column] != value;
default:
break;
}
return true;
};
for(i=5;i<query.count;i++)
{
if (ps[i].value.All(char.IsDigit))
{
query.numValue = float.Parse(ps[i].value);
}
else
{
query.filterValue = ps[i].value;
}
query.filterColumn = ps[i].col;
query.opr = ps[i].opr;
query.opr = query.opr.ToLower();
IEnumerable<ShoeModels> res = null;
if (query.opr == "gt" || query.opr == "lt" || query.opr == "gte" || query.opr == "lte" || query.opr == "ne")
{
IEnumerable<ShoeModels> tuyo = from p in loopData.Where(a => comparer(a, query.filterColumn, query.opr, query.numValue))
select p;
res = tuyo.ToList();
}
else if (query.opr.ToLower() == "like")
{
IEnumerable<ShoeModels> tuyo = from p in loopData.Where(a => a[query.filterColumn].Contains(query.filterValue))
select p;
res = tuyo.ToList();
}
finalResult = res.ToList();
}
正如您在代码中看到的那样,我使用 LHS 方括号运算符来过滤我的数据。现在,如果我在比较之前使用我的 like 运算符,我会得到一个异常 "float does not contain definition for Contains"。 所以我通过阅读延迟执行然后在每次执行后将 ToList() 添加到结果来解决这个问题。所以现在有一个问题,每当我尝试过滤代码时,只有最后一个过滤器有效,但所有过滤器都应该有效。
您只有最后一个过滤器起作用,因为您在 for
循环的每次迭代中将过滤器应用于 loopData
:
from p in loopData.Where()...
因此,每次您过滤初始 loopData
而不是上次过滤产生的数据。
您需要将每次迭代的初步过滤结果保存在一些其他变量中,这些变量应该在 for
循环之外声明(在我的代码示例中,这个变量是 res
变量)并应用每次迭代中该 res
变量的其他过滤器。
List<ShoeModels> res = loopData; // here I declare and assign res variable, it should be outside of the loop
for (int i = 5; i < query.count; i++)
{
if (ps[i].value.All(char.IsDigit))
{
query.numValue = float.Parse(ps[i].value);
}
else
{
query.filterValue = ps[i].value;
}
query.filterColumn = ps[i].col;
query.opr = ps[i].opr;
query.opr = query.opr.ToLower();
IEnumerable<ShoeModels> tuyo = null;
if (query.opr == "gt" || query.opr == "lt" || query.opr == "gte" || query.opr == "lte" || query.opr == "ne")
{
// apply filtering to the res variable
tuyo = res.Where(a => comparer(a, query.filterColumn, query.opr, query.numValue));
}
else if (query.opr.ToLower() == "like")
{
// apply filtering to the res variable
tuyo = res.Where(a => a[query.filterColumn].Contains(query.filterValue));
}
// update res variable, so on the next iteration we will operate on updated (filtered) list
res = tuyo.ToList();
}
使用 float
来表达价格或金钱是一种不好的做法。使用 decimal
类型。
我在 LinqPad5 中创建了一个演示(如果您不了解 LinqPad,请安装并使用它 - 它非常有用,尤其是对于编写 LINQ 查询):
void Main()
{
var ps = new List<(string col, string opr, string value)>();
ps.Add(("0", "gt", "6"));
ps.Add(("1", "like", "30.0"));
ps.Add(("2", "ne", "60"));
var loopData = new List<ShoeModels> {
new ShoeModels { Range = new[] { 5.0m, 10.0m, 15.0m }}, // leaves out on first filter ps[0]
new ShoeModels { Range = new[] { 10.0m, 20.0m, 30.0m }},// leaves out on second filter ps[1]
new ShoeModels { Range = new[] { 10.0m, 30.0m, 30.0m }},// this is the final result of all three filters
new ShoeModels { Range = new[] { 15.0m, 30.0m, 60.0m }},// leaves out on third filter ps[2]
};
loopData.Dump("initial data");
List<ShoeModels> res = loopData;
var query = new Query();
for (int i = 0; i < ps.Count; i++)
{
decimal number;
if (Decimal.TryParse(ps[i].value, out number))
{
query.numValue = number;
}
else
{
query.filterValue = ps[i].value;
}
query.filterColumn = ps[i].col;
query.opr = ps[i].opr.ToLower();
IEnumerable<ShoeModels> tuyo = null;
if (query.opr == "gt" || query.opr == "lt" || query.opr == "gte" || query.opr == "lte" || query.opr == "ne")
{
tuyo = res.Where(a => comparer(a, query.filterColumn, query.opr, query.numValue));
}
else if (query.opr.ToLower() == "like")
{
tuyo = res.Where(a => (a[query.filterColumn]).ToString(CultureInfo.InvariantCulture).Contains(query.filterValue));
}
res = tuyo.ToList();
res.Dump("after " + i + " iteration");
}
res.Dump("final result");
}
private Func<ShoeModels, string, string, decimal, bool> comparer = (ShoeModels a, string column, string op, decimal value) =>
{
switch (op)
{
case "lt": return a[column] < value;
case "gt": return a[column] > value;
case "gte": return a[column] >= value;
case "lte": return a[column] <= value;
case "ne": return a[column] != value;
default:
break;
}
return true;
};
class Query {
public decimal numValue { get; set;}
public string filterValue { get; set;}
public string filterColumn {get; set;}
public string opr {get; set;}
}
class ShoeModels {
public decimal[] Range = new decimal[3];
public decimal this[string index] {
get => Range[int.Parse(index)];
}
public override string ToString() =>
$"{Range[0].ToString(CultureInfo.InvariantCulture)} " +
$"{Range[1].ToString(CultureInfo.InvariantCulture)} " +
$"{Range[2].ToString(CultureInfo.InvariantCulture)}";
}