Case-insensitive matching of a string to a Java enum
JavaGenericsEnumsJava 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())); }