1

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.

snappymcsnap
  • 2,050
  • 2
  • 29
  • 53
  • 1
    Your code doesn't compile. Particularly, `Filter` is not found. Please make sure to include all relevant code that would allow us to test your code _is this question_. – rory.ap Aug 22 '23 at 12:58
  • 3
    So just to confirm: You can't use compile-time types for this? So you can't use a normal filter along the lines of `people.Where(person => person.FirstName == "John" && person.LastName == "Smith")` ? – Matthew Watson Aug 22 '23 at 13:00
  • 1
    @rory.ap It should be clear that they're using the `Filter` method from the question they linked... – canton7 Aug 22 '23 at 13:02
  • 1
    @canton7 you have enough rep to know that's not acceptable when writing a question. The relevant code should be included in [an MCVE/MRE](https://stackoverflow.com/help/minimal-reproducible-example). – rory.ap Aug 22 '23 at 13:03
  • @canton7 I neither DVd nor voted to close. I'm just pointing out what needs to be the case to fit within the guidelines of this site. Maybe my motive is to avoid the OP from having to deal with others who may come along and vote... – rory.ap Aug 22 '23 at 13:07
  • @canton7 @rory.ap I have submitted an edit where it clarifies the origin of `Filter`. There is no need for either of you to "argue". – Roe Aug 22 '23 at 13:09
  • @Roe The problem with that is if the other question gets deleted, it invalidates this one too. – DavidG Aug 22 '23 at 13:10
  • @DavidG I agree with this, but think this is OP's "responsibility" to make sure their question is not dependent on other sources. Although in this case I don't think compressing the linked source will make for a clear explanation – Roe Aug 22 '23 at 13:12
  • 1
    @snappymcsnap Sorry if I called your questions stupid. Wait, did I? There are reasons for the rules they've made (I didn't make them, by the way). One excellent reason is what DavidG said: the other question could be deleted. You'll find plenty of rude people on this site, I'm sure you know, but I'm simply trying to help you avoid them -- by making your question less-attractive to them by simply following the rules. Again, I didn't down-vote or vote to close, so I don't know how gentler I could have been...how I could have "given you a break". – rory.ap Aug 22 '23 at 13:18

2 Answers2

2

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.

DavidG
  • 113,891
  • 12
  • 217
  • 223
  • thanks for the quick answer, I'm getting a compile error on the exp.AndAlso(predicate) saying no overload of AndAlso takes one parameter. Is there a package I'm missing maybe? Thanks – snappymcsnap Aug 22 '23 at 13:17
  • I linked the implementation of the `AndAlso` method, have you imported it? – DavidG Aug 22 '23 at 13:18
  • my bad, I see it now, thank you, i'll give it a whirl! – snappymcsnap Aug 22 '23 at 13:20
  • Creating lambdas to combine them by custom extension looks like overkill. – Svyatoslav Danyliv Aug 22 '23 at 17:07
  • @SvyatoslavDanyliv I don't completely disagree, but I'm only answering the question that was asked here. And I can think of a few (perhaps niche) situations where this can be really useful. – DavidG Aug 22 '23 at 17:34
  • Yes, it useful method, but not here. This answer shows how to do that with additional not needed `N*3` allocations and replacing visitor work. And actually complicated this answer. – Svyatoslav Danyliv Aug 22 '23 at 18:01
-2
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 TestData=people.Where(o=>o.FirstName=="John" && o.LastName == "Smith" ).ToList();

You get Result List on "TestData"

Kabilu
  • 101
  • 10