如何将LINQ用于循环中的多个过滤器?

时间:2019-06-11 11:48:45

标签: c# linq linq-to-entities

我正在使用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不包含Contains的定义”。 因此,我通过阅读有关延迟执行的内容,然后在每次执行后将ToList()添加到结果中来解决了该问题。因此,现在存在一个问题,每当我尝试过滤代码时,只有最后一个过滤器可以工作,但是所有过滤器都应该工作。

1 个答案:

答案 0 :(得分:0)

由于在loopData循环的每次迭代中将过滤器应用于for,所以只有最后一个过滤器起作用:

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)}";
}