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.