Combine Multiple Predicates

Solution 1:

How about:

public static Predicate<T> Or<T>(params Predicate<T>[] predicates)
{
    return delegate (T item)
    {
        foreach (Predicate<T> predicate in predicates)
        {
            if (predicate(item))
            {
                return true;
            }
        }
        return false;
    };
}

And for completeness:

public static Predicate<T> And<T>(params Predicate<T>[] predicates)
{
    return delegate (T item)
    {
        foreach (Predicate<T> predicate in predicates)
        {
            if (!predicate(item))
            {
                return false;
            }
        }
        return true;
    };
}

Then call it with:

List<string> filteredNames = names.FindAll(Helpers.Or(StartsWithE, StartsWithI));

Another alternative would be to use multicast delegates and then split them using GetInvocationList(), then do the same thing. Then you could do:

List<string> filteredNames = names.FindAll(Helpers.Or(StartsWithE+StartsWithI));

I'm not a huge fan of the latter approach though - it feels like a bit of an abuse of multicasting.

Solution 2:

I guess you could write something like this:

Func<string, bool> predicate1 = s => s.StartsWith("E");
Func<string, bool> predicate2 = s => s.StartsWith("I");
Func<string, bool> combinedOr = s => (predicate1(s) || predicate2(s));
Func<string, bool> combinedAnd = s => (predicate1(s) && predicate2(s));

... and so on.

Solution 3:

I just recently came up with a solution similar to this problem, which could be also helpful. I expanded the FindAll method for lists, allowing me to stack predicates in lists as I needed:

public static class ExtensionMethods
{
    public static List<T> FindAll<T> (this List<T> list, List<Predicate<T>> predicates)
    {
        List<T> L = new List<T> ();
        foreach (T item in list)
        {
            bool pass = true;
            foreach (Predicate<T> p in predicates)
            {
                if (!(p (item)))
                {
                    pass = false;
                    break;
                }
            }
            if (pass) L.Add (item);
        }
        return L;
    }
}

It returns a list with only the items matching all given predicates. Of course it can easily be altered to OR all predicates instead of AND. But with that alone one can assemble quite a good variety of logical combinations.

Usage:

{
    List<Predicate<int>> P = new List<Predicate<int>> ();
    P.Add (j => j > 100);
    P.Add (j => j % 5 == 0 || j % 7 == 0);
    P.Add (j => j < 1000);

    List<int> L = new List<int> () { 0, 1, 2, ... 999, 1000 }
    List<int> result = L.FindAll (P);

    // result will contain: 105, 110, 112, 115, 119, 120, ... 994, 995 
}