Как использовать LINQ для нескольких фильтров внутри цикла? - PullRequest
0 голосов
/ 11 июня 2019

Я использую 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 голосов
/ 11 июня 2019

Работает только последний фильтр, потому что вы применяете свои фильтры к 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 type.

Я создал демонстрационную версию в 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)}";
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...