How to xor predicates?

Viewed 324

I need a method where I need to xor Predicates which I will recieve as method params. I have a somewhat working but cumbersome solution for two predicates. To give a simple, minimal and reproducible example:

Predicate<String> pred1  = s -> s.contains("foo");
Predicate<String> pred2  = s -> s.contains("bar");
    
String toTest = "foobar";

The logical OR will return true for given predicates and the test string:

boolean oneOnly   = pred1.or(pred2).test(toTest);

but for my use case it should return false since both substrings are included. It should only return true if and only if one condition is met.

For two prdeicates I have this

static boolean xor(Predicate<String> pred1, Predicate<String> pred2, String toTest){
    return pred1.and(pred2.negate()).or(pred2.and(pred1.negate())).test(toTest);
}

Is there a simple but a convinient way to xor predicates?

3 Answers

In followup to @xdhmoore's answer, that's overkill and can be done much simpler:

static <T> Predicate<T> xor(Predicate<T> pred1, Predicate<T> pred2) {
    return t -> pred1.test(t) ^ pred2.test(t);
}

Update:

Below are some examples of why you'd want to return a Predicate instead of a boolean, but @rzwitserloot's answer does it nicely and more succinctly.


To play the Devil's advocate: it's less pretty, but one advantage for the way you already have it is you are slightly more in line with the Predicate idioms. A little tweaking gets you:

Return a Predicate

static <T> Predicate<T> xor(Predicate<T> pred1, Predicate<T> pred2){
    return pred1.and(pred2.negate())
       .or(pred2.and(pred1.negate()));
}

// Which means you can do this, which is probably more conducive to combining your
// new xor function with other predicates:
xor((Integer a) -> a > 1, (Integer b) -> b < 10).test(0));

// For example, because you return a Predicate:
xor((Integer a) -> a > 1, (Integer b) -> b < 10).negate().test(0));

Return a boolean

static <T> boolean xor(Predicate<T> pred1, Predicate<T> pred2, T toTest) {
    return pred1.test(toTest) ^ pred2.test(toTest);
}

// In contrast, if your xor function returns a boolean, you get this, which is 
// similar, but is less conducive to using all the Predicate methods:
xor((Integer a) -> a > 1, (Integer b) -> b < 10, 14);

// To be honest, this seems more readable to me than the negate() function in the
// example above, but perhaps there are scenarios where the above is preferred...
!xor((Integer a) -> a > 1, (Integer b) -> b < 10, 14)

Not a big deal, but your question made me curious...

You could reduce your xor'ed predicates to a single predicate with stream.reduce and then return the outcome.

like so:

import java.util.function.Predicate;
import java.util.Arrays;

public class MultiXor{

    public static void main(String[] args){
            System.out.println(xor("monkey", p -> p.equals("monkey"), p -> p.equals("dork"), p -> p.equalsIgnoreCase("Monkey")) );
            System.out.println(true ^ false ^ true);
            System.out.println(xor("monkey", p -> p.equals("monkey"), p -> p.equals("dork")) );
            System.out.println(true ^ false);

    }

    public static <T> boolean xor(final T param, Predicate<T>... predicates){
            return Arrays.stream(predicates).reduce( p -> false, (previous, p) -> r -> previous.test(param) ^ (p.test(param))).test(param);
    }

}

Related