1

I have the Enum A, B, C. In a method, I am given two different Enum and must return the remaining Enum.

Example: I receive A and C I must return B

The solution that I have is to use if elseif else:

private EnumABC findRemaining(EnumABC pEnum1, EnumABC pEnum2){ if((pEnum1 == EnumABC.A || pEnum2 == EnumABC.A) && (pEnum1 == EnumABC.B || pEnum2 == EnumABC.B)){ return EnumABC.C; } else if((pEnum1 == EnumABC.A || pEnum2 == EnumABC.A) && (pEnum1 == EnumABC.C || pEnum2 == EnumABC.C)){ return EnumABC.B; } else{ return EnumABC.A; } } 

I was wondering if there was a more readable solution then this.

2
  • 1
    Is enum1 guaranteed to be different from enum2? If enum1 is the same as enum2 then what do you return? A more readable solution would be to put it in several statements instead of a one liner Commented Feb 15, 2024 at 19:36
  • Yes the enum1 and enum2 are guaranteed to be different. Commented Feb 15, 2024 at 19:39

5 Answers 5

9

tl;dr

enum Animal { DOG , CAT , BIRD } EnumSet<Animal> flyingAnimals = EnumSet.complementOf ( EnumSet.of( DOG , CAT ) ) 

EnumSet.complementOf

You can make a Set of enum objects. Indeed, you can make a highly performant EnumSet.

enum Animal { DOG , CAT , BIRD } EnumSet<Animal> furry = EnumSet.of( DOG , CAT ) ; 

Ask for those enum objects not found in an EnumSet by calling the static method EnumSet.complementOf.

EnumSet<Animal> nonFurry = EnumSet.complementOf ( furry ) ; 

Tip: You can define an enum locally in modern Java. Ditto for interface and record.

Sign up to request clarification or add additional context in comments.

2 Comments

I assume the asker cannot do this, since the question states “The solution that I have is to use if elseif else.”
@VGR should be valid since the OP says "the solution that I have is to use" not "the solution that I have to use"
3

While Basil's answer is the most elegant one, this one iterates and is rather readable:

enum EnumABC { A,B,C; EnumABC findRemaining(EnumABC other) { return Arrays.stream(values()) .filter(v -> v!=this&&v!=other) .findFirst() .get(); } } 

This is now a method of the enum, so instead of

EnumABC remaining = findRemaining(pEnum1, pEnum2); 

you have to call it using

EnumABC remaining = pEnum1.findRemaining(pEnum2); 

Comments

2

If you are using integers

int a = 1, b = 2, c = 3; int findMissing(int e1, int e2) { return a + b + c - e1 - e2; } 

There are lots of "don't do it that way" discussions about using ordinals and value but if you only have 3 values, it could be clumsily coded as

enum Letter {A, B, C}; Letter findMissing(Letter e1, Letter e2) { int missing = A.ordinal() + B.ordinal() + C.ordinal() - e1.ordinal() - e2.ordinal(); return Letter.values()[missing]; } 

1 Comment

Alternatively, you could do return a ^ b ^ c ^ e1 ^ e2; (and ordinal equivalent), which is pleasing because it's using the same operation over and over.
0

Just to throw another idea into the mix, you can exploit the comparability of enums:

EnumABC[] args = {pEnum1, pEnum2}; Arrays.sort(args, Comparator.naturalOrder()); // Sort by ordinal. EnumABC[] vals = EnumABC.values(); // Already sorted by ordinal. int i = 0; while (i < args.length && args[i] == vals[i]) { ++i; } return vals[i]; 

This looks for the first element of EnumABC.values() which doesn't equal an element in args - because both arrays are sorted, you can just do a linear probe to find that.

Comments

-1

More general solution for enums with arbitrary amount of values using maths, varargs and reflection

Enums all have ordinal values starting from 0 to n. So using that knowledge we can sum up all the ordinal values to a total and substract the ordinal values of the provided enums. If we mix in varargs and some reflextion that works for any enum with an arbitrary amount of values.

Better yet, you can compute the sum without iterating through all the values if you want. Since the the sum of the first n natural numbers is given by (n * (n + 1)) / 2.

Therefore the following would work. You could even use varargs to

import java.lang.reflect.InvocationTargetException; import java.util.Arrays; enum MyEnumWith3Values { A, B, C } enum MyEnumWith4Values { A, B, C, D } public class Main { public static void main(String[] args) throws InvocationTargetException, NoSuchMethodException, IllegalAccessException { // For enum with 3 values System.out.println(RemainingEnumValue(MyEnumWith3Values.A, MyEnumWith3Values.B)); System.out.println(RemainingEnumValue(MyEnumWith3Values.A, MyEnumWith3Values.C)); System.out.println(RemainingEnumValue(MyEnumWith3Values.C, MyEnumWith3Values.B)); // For enum with 4 values System.out.println(RemainingEnumValue(MyEnumWith4Values.A, MyEnumWith4Values.B, MyEnumWith4Values.C)); } public static <T extends Enum<T>> Enum<T> RemainingEnumValue(T... enumValues) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { if(enumValues.length < 1) throw new IllegalArgumentException("At least one enum value!"); var enumClazz = enumValues[0].getClass(); var valuesMethod = enumClazz.getMethod("values"); var enumAllValues = (Enum<T>[])valuesMethod.invoke(enumClazz); if(enumAllValues.length - 1 != enumValues.length) throw new IllegalArgumentException("You must provide exactly one less then all the available enum values"); var maxEnumValue = enumAllValues.length - 1; var enumOrdinalTotal = (maxEnumValue * maxEnumValue + maxEnumValue) >> 1; var totalProvidedEnums = Arrays.stream(enumValues).map(Enum::ordinal).reduce(0, Integer::sum); return enumAllValues[enumOrdinalTotal - totalProvidedEnums]; } } 

Remark on this solution

I've added this solution since I think it's an interesting and different approach which uses a mathematical idea (which I always find interesting).

However, due to reflection and MyEnum.values() calls being involved it's certainly not the most performant solution. It's also not the most straightforward, robust or maintainable version of all the answers here. Lastly, you shouldn't really use ordinal() (See this SO thread as it can cause some problems.

Without reflection and with cached MyEnum.values()

If don't need it to be generic (=> eliminates reflection) and cache the MyEnum.values() call, you could however also make this quite a bit more performant. You'd still be using ordinal() though.

enum MyEnumWith3Values { A, B, C } public class Main { private static MyEnumWith3Values[] cachedEnumValues = MyEnumWith3Values.values(); public static void main(String[] args){ // For enum with 3 values System.out.println(RemainingEnumValue(MyEnumWith3Values.A, MyEnumWith3Values.B)); System.out.println(RemainingEnumValue(MyEnumWith3Values.A, MyEnumWith3Values.C)); System.out.println(RemainingEnumValue(MyEnumWith3Values.C, MyEnumWith3Values.B)); } public static MyEnumWith3Values RemainingEnumValue(MyEnumWith3Values a, MyEnumWith3Values b){ var maxEnumValue = cachedEnumValues.length - 1; var enumOrdinalTotal = (maxEnumValue * maxEnumValue + maxEnumValue) >> 1; var totalProvidedEnums = a.ordinal() + b.ordinal(); return cachedEnumValues[enumOrdinalTotal - totalProvidedEnums]; } } 

3 Comments

Surely you jest?
@user207421 I did (assuming you mean test). And it worked. Just somehow didn't copy over the imports. I've included them now.
@user207421 in case that wasn't a typo. No, not really. I know it's certainly far from the most elegant, robust or maintainable solution, but it's a working solution which I thought, as I mentioned, was using a different and interesting mathematical idea. Therefore I threw it out there. Happy to include your concerns in the answer or more prominently state them. Again, I am not trying to sell this as the go-to answer, just another one.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.