Playing with Predicate
I came across a problem during recent round of refactoring. Here's a contrived simiIar example.
I have an IEnumerable, MyThings contains a property of type Enum called MyThingType. Scattered around my class I found I had quite a bit of duplicated code consisting of a Linq Where statement to filter the IEnumerable based on a specific values of MyThingType and then perform some additional actions on the result.
var filtereredThings = MyThingsList.Where(t => t.MyThingType == ThingType.ThingA); // other stuff on filteredThings
All good so far, I extracted the common code into a new private method call which returned my result and took the MyThingType as a parameter to filter on, the problem arose when my last Where statement filtered on two MyThingType values.
private IEnumerable FilterAndProcessThingType(IEnumerable data, ThingType filterType)
{
var filtereredThings = MyThingsList.Where(t => t.MyThingType == filterType);
// other stuff on filteredThings and return result
}
I changed my method to take a params parameter for the filter so I can pass a variable number of MyThingType in, but how do I change my Linq?
private IEnumerable FilterAndProcessThingType(IEnumerable data, params ThingType[] filterType)
{
var filtereredThings = MyThingsList.Where(t => t.MyThingType == ???);
// other stuff on filteredThings and return result
}
This is where Predicate comes in; a Predicate is a function which returns true or false. This means I can dynamically create one to do my MyThingType check and put it in my Where's Lambda expression.
private IEnumerable FilterAndProcessThingType(IEnumerable data, params ThingType[] typeFilter)
{
var thingTypeMatcher = new Predicate<tuple<thingtype, thingtype[]="">>(t =>
{
var thingType = t.Item1;
var thingTypeArray = t.Item2;
return thingTypeArray.Aggregate(false,(current, type) => current | thingType == type);
});
var filtereredThings = MyThingsList.Where(t => thingTypeMatcher(new Tuple<thingtype, thingtype[]="">(t.MyThingType, typeFilter));
// other stuff on filteredThings and return result
}
And that's it, now I can pass a variable number of types to match into my common code.