I am using linq for filtering my data, i.e. if the data is of numeric type i have to use comparators else I need string matching.
I am currently able to get only one filter working even though my linq queries are inside loop.
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();
}
As you can see in the code i am using LHS square bracket operators to filter my data. Now if i use my like operator before comparing i am getting an exception "float does not contain definition for Contains". So i solved that by reading about Deferred Execution and then by adding ToList() to the result after every execution. So now there is a problem that whenever i try to filter the code only the last filter works, but all the filters should work.
You get only the last filter working because you apply your filters to loopData
in each iteration of for
loop:
from p in loopData.Where()...
So, each time you're filtering your initial loopData
but not the data resulted from the previous filtering.
You need to save preliminary result of filtering on each iteration in some other variable that should be declared outside of the for
loop (in my code example this variable is res
variable) and apply other filters to that res
variable in each iteration.
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();
}
To use float
to express prices or money is a bad practice. Use decimal
type.
I created a demo in LinqPad5 (if you don't know about LinqPad please install it and use it - it's very useful, especially for writing LINQ queries):
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)}";
}