Case-insensitive matching of a string to a Java enum

JavaGenericsEnums

Java Problem Overview


Java provides a valueOf() method for every Enum<T> object, so given an enum like

public enum Day {
  Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday
}

one can do a lookup like

Day day = Day.valueOf("Monday");

If the string passed to valueOf() does not match (case sensitive) an existing Day value, an IllegalArgumentException is thrown.

To do a case-insensitive matching, one can write a custom method inside the Day enum, e.g.

public static Day lookup(String day) {
  for (Day d : Day.values()) {
    if (d.name().equalsIgnoreCase(day)) {
      return type;
    }
  }
  return null;
}

Is there any generic way, without using caching of values or any other extra objects, to write a static lookup() method like the above only once (i.e., not for every enum), given that the values() method is implicitly added to the Enum<E> class at compile time?

The signature of such a "generic" lookup() method would be similar to the Enum.valueOf() method, i.e.:

public static <T extends Enum<T>> T lookup(Class<T> enumType, String name);

and it would implement exactly the functionality of the Day.lookup() method for any enum, without the need to re-write the same method for each enum.

Java Solutions


Solution 1 - Java

I found getting the special blend of generics a little tricky, but this works.

public static <T extends Enum<?>> T searchEnum(Class<T> enumeration,
		String search) {
	for (T each : enumeration.getEnumConstants()) {
		if (each.name().compareToIgnoreCase(search) == 0) {
			return each;
		}
	}
	return null;
}

Example

public enum Horse {
	THREE_LEG_JOE, GLUE_FACTORY
};

public static void main(String[] args) {
	System.out.println(searchEnum(Horse.class, "Three_Leg_Joe"));
	System.out.println(searchEnum(Day.class, "ThUrSdAy"));
}

Solution 2 - Java

I would think the easiest safe way to do it would be:

Arrays.stream(Day.values())
    .filter(e -> e.name().equalsIgnoreCase(dayName)).findAny().orElse(null);

Or if you want to use the class object, then:

Arrays.stream(enumClass.getEnumConstants())
    .filter(e -> (Enum)e.name().equalsIgnoreCase(dayName)).findAny().orElse(null);

Solution 3 - Java

Starting from version 3.8 apache commons-lang EnumUtils has two handy methods for this:

  • getEnumIgnoreCase(final Class<E> enumClass, final String enumName)
  • isValidEnumIgnoreCase(final Class<E> enumClass, final String enumName)

Solution 4 - Java

A generic solution would be to keeo to the convention that constants are uppercase. (Or in your specific case use a capitalize on the look-up string).

public static <E extends Enum<E>> E lookup(Class<E> enumClass,
        String value) {
    String canonicalValue.toUpperCase().replace(' ', '_');
    return Enum<E>.valueOf(enumClass, canonicalValue);
}

enum Day(MONDAY, ...);
Day d = lookup(Day,class, "thursday");

Solution 5 - Java

For Android and relatively short Enums, I do the simple loop and compare the name ignoring the case.

public enum TransactionStatuses {
    public static TransactionStatuses from(String name) {
        for (TransactionStatuses status : TransactionStatuses.values()) {
            if (status.name().equalsIgnoreCase(name)) {
                return status;
            }
        }
        return null;
    }
}

Solution 6 - Java

You can use Class's getEnumConstants() method, which returns an array of all the enum types, if the Class represents an enum, or null if not.

> Returns the elements of this enum class or null if this Class object does not represent an enum type.

Your enhanced for loop line would look like this:

for (T d : enumType.getEnumConstants()) {

Solution 7 - Java

I can believe this, or a similar solution hasn't been posted yet. My preferred go-to here (there is absolutely no need for a 'lookup', just a smarter valueOf. Plus, as a bonus, enum values are all uppercase, as us former c++'ers think they should be...

public enum Day {
    MONDAY("Monday"), 
    TUESDAY("Tuesday"), 
    WEDNESDAY("Wednesday"),
    THURSDAY("Thursday"), 
    FRIDAY("Friday"),
    SATURDAY("Saturday"),
    SUNDAY("Sunday"); 

    public static Day valueOfIgnoreCase(String name) {
         return valueOf(name.toUpperCase());
    }

    private final String displayName; 

    Day(String displayName) {
        this.displayName = displayName;
    }

    @Override
    public String toString() {
        return this.displayName; 
    }
}

And then:

Day day = Day.valueOfIgnoreCase("mOnDay"); 
System.out.println(day); 

>>> Monday

Solution 8 - Java

> and it would implement exactly the functionality of the Day.lookup() > method for any enum, without the need to re-write the same method for > each enum.

Probably you can write a utility class for doing that as the following.

public class EnumUtil {

    private EnumUtil(){
        //A utility class
    }

    public static <T extends Enum<?>> T lookup(Class<T> enumType,
                                                   String name) {
        for (T enumn : enumType.getEnumConstants()) {
            if (enumn.name().equalsIgnoreCase(name)) {
                return enumn;
            }
        }
        return null;
    }

    // Just for testing
    public static void main(String[] args) {
        System.out.println(EnumUtil.lookup(Day.class, "friday"));
        System.out.println(EnumUtil.lookup(Day.class, "FrIdAy"));
    }

}

enum Day {
    Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday
}

It would have been nice if there was a way in Java for us to extend the Enum class by implicitly adding methods just the way values() method is added but I don't think there is a way to do that.

Solution 9 - Java

I haven't tested this yet but why not overloading these methods as mentioned in this SO answer

public enum Regular {
    NONE,
    HOURLY,
    DAILY,
    WEEKLY;

    public String getName() {
        return this.name().toLowerCase();
    }    
}

Solution 10 - Java

I am using this way for case-insensitive matching of a string to a java enum Day[] days = Day.values(); for(Day day: days) { System.out.println("MONDAY".equalsIgnoreCase(day.name())); }

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionPNSView Question on Stackoverflow
Solution 1 - JavaAdamView Answer on Stackoverflow
Solution 2 - JavasprinterView Answer on Stackoverflow
Solution 3 - JavaEnigoView Answer on Stackoverflow
Solution 4 - JavaJoop EggenView Answer on Stackoverflow
Solution 5 - JavaAlen SiljakView Answer on Stackoverflow
Solution 6 - JavargettmanView Answer on Stackoverflow
Solution 7 - JavagbtimmonView Answer on Stackoverflow
Solution 8 - JavaNandanaView Answer on Stackoverflow
Solution 9 - JavaAhmed HamedView Answer on Stackoverflow
Solution 10 - JavaAtul JainView Answer on Stackoverflow