For reference, here is the original question (Keep in mind that the Filter()
function originates from this post): Dynamic Where for List<T>
Original post's function for clarity:
public static List<T> Filter<T>
(this List<T> source, string columnName,
string compValue)
{
ParameterExpression parameter = Expression.Parameter(typeof(T), "x");
Expression property = Expression.Property(parameter, columnName);
Expression constant = Expression.Constant(compValue);
Expression equality = Expression.Equal(property, constant);
Expression<Func<T, bool>> predicate =
Expression.Lambda<Func<T, bool>>(equality, parameter);
Func<T, bool> compiled = predicate.Compile();
return source.Where(compiled).ToList();
}
Which allows you to do this:
var people = new[] {
new { FirstName = "John", LastName = "Smith" },
new { FirstName = "John", LastName = "Smith" },
new { FirstName = "John", LastName = "Noakes" },
new { FirstName = "Linda", LastName = "Smith" },
new { FirstName = "Richard", LastName = "Smith" },
new { FirstName = "Richard", LastName = "Littlejohn" },
}.ToList();
var filtered = people.Filter("LastName", "Smith");
But what if you have multiple properties you want to match on? For example if I wanted to filter for all people with FirstName = John and LastName = Smith
Pseduocode just to give the idea, I'm sure there's a more elegant way to code this:
var filtered = people.Filter(new [] {Column="FirstName", Value = "John"}, {Column="LastName", Value="Smith"});
Also curious if this could be easily adapted to handle any type for the property (i.e. object instead of always string like the example). I haven't worked with these expression trees much, I appreciate any help you can provide.
You can combine multiple expressions using the AndAlso
extension found here. Then change your filter method to this:
public static List<T> Filter<T>(this List<T> source, params (string columnName, string compValue)[] filters)
{
Expression<Func<T, bool>> exp = null;
foreach (var filter in filters)
{
ParameterExpression parameter = Expression.Parameter(typeof(T), "x");
Expression property = Expression.Property(parameter, filter.columnName);
Expression constant = Expression.Constant(filter.compValue);
Expression equality = Expression.Equal(property, constant);
Expression<Func<T, bool>> predicate = Expression.Lambda<Func<T, bool>>(equality, parameter);
exp = exp is null ? predicate : exp.AndAlso(predicate);
}
Func<T, bool> compiled = exp.Compile();
return source.Where(compiled).ToList();
}
And call it like this:
var results = people.Filter(("FirstName", "John"), ("LastName", "Smith"));
This was very quickly written so you may prefer not to use a list of tuples.