I have a list of objects. I have a list of filter values. I need a count of the list of objects where a field/property (specified at run-time via variable) has a value found in the filter list. BTW there are other "filtered" properties as well but these property names are "fixed" and therefore are known at build-time.
Let's say the filter list has the following values:
Orange, Yellow, Blue
Let's say the object list has the following properties/values:
Name: Bike, Color: Red |
Name: Car, Color: Green |
Name: Box, Color: Yellow |
Name: Door, Color: Orange |
The "filter field" is string variable defined at run-time to be the "color" property/field.
The result should be 2 (box and door) because the other objects in the list do not have a "color" that is found in the filter list.
Here is what I have and it works but I am looking for a way to dynamically specify the "filter field" property name ("Field1") at run-time.
total = (from objects in ObjectsObservableCollection where ((object.InCity && cityList.Select(ma => ma.CityText).Contains(objects.CityLabel)) && FilterValuesList.Select(ff => ff.ToString().ToUpper()).Contains(objects.**Field1**.ToString().ToUpper())) select objects.ID).Count();
I could use a switch-case (with a select statement for each property) to determine which property is being used as the "filter field" at run-time (because all of the property names are known at run-time) but I really want to understand how to do it "the right way".
I think Expressions is the right way to go here but I can't wrap my head around the syntax. If it was 1 value I was searching (instead of a list of values), I think I could do it.
Can anyone give me a hint, maybe point to an article or tutorial or perhaps explain what I need to use to accomplish this?
CodePudding user response:
If you already have a fixed set of fields you will want to test, and assuming they are all string
fields, you can create a Dictionary
of accessor lambdas that can be looked up by field name:
var FilterValues = FilterValueList.Select(s => s.ToLowerInvariant()).ToHashSet();
var TransportAccessorsDict = new[] {
new { FieldName = "Color", Accessor = (Func<Transport,String>)(t => t.Color) },
new { FieldName = "Name", Accessor = (Func<Transport,String>)(t => t.Name) },
}
.ToDictionary(fa => fa.FieldName, fa => fa.Accessor);
var getFilterField = TransportAccessorsDict[FilterField];
var total = (from objects in ObjectsObservableCollection
where FilterValues.Contains(getFilterField(objects).ToLowerInvariant())
select objects.ID
).Count();
This should be reasonably performant, though if performance is a big consideration, you should probably be converting the FilterValuesList to an uppercase (I think lowercase is preferred though both are bad, a culture aware string insensitive contains would be best) HashSet
before using it in the LINQ query.
I removed the ToString
calls as they should be redundant, otherwise you have bigger (type) issues.