26

Why there is no contains method in enum classes (as well as in Java)? And how to implement it elegantly? Now I'm using this ugly approach:

val contains: Boolean = 
    try {
        MyEnum.valueOf("some string")
        true
    } catch (e: IllegalArgumentException) {
        false
    }
Maksim Ostrovidov
  • 10,720
  • 8
  • 42
  • 57

4 Answers4

61

enumContains

You can create an enumContains function similar to Hound Dog's answer but using reified type parameters instead.

You cannot create a JVM independent solution in Kotlin 1.0 but you can in Kotlin 1.1 using enumValues.

Kotlin 1.1

/**
 * Returns `true` if enum T contains an entry with the specified name.
 */
inline fun <reified T : Enum<T>> enumContains(name: String): Boolean {
    return enumValues<T>().any { it.name == name}
}

Kotlin 1.0

/**
 * Returns `true` if enum T contains an entry with the specified name.
 */
inline fun <reified T : Enum<T>> enumContains(name: String): Boolean {
    return T::class.java.enumConstants.any { it.name == name}
}

Usage

enumContains<MyEnum>("some string") // returns true or false

enumValueOfOrNull

If you also need the actual enum entry then you might consider creating an enumValueOfOrNull function instead.

Kotlin 1.1

/**
 * Returns an enum entry with the specified name or `null` if no such entry was found.
 */
inline fun <reified T : Enum<T>> enumValueOfOrNull(name: String): T? {
    return enumValues<T>().find { it.name == name }
}

Kotlin 1.0

/**
 * Returns an enum entry with the specified name or `null` if no such entry was found.
 */
inline fun <reified T : Enum<T>> enumValueOfOrNull(name: String): T? {
    return T::class.java.enumConstants.find { it.name == name }
}

Usage

enumValueOfOrNull<MyEnum>("some string")
Community
  • 1
  • 1
mfulton26
  • 29,956
  • 6
  • 64
  • 88
  • 2
    Yeah that's much better using `reified` (having an extension function on KClass isn't very pretty!). On a semi-related side note, along with `enumConstants`, 1.1 is bringing a whole bag of [reflection goodies](https://github.com/JetBrains/kotlin/commit/ed1490dbc43f88696f82e5307df43269ecbb32b1)! – James Bassett Jan 25 '17 at 22:39
18

You can just take values Array of your enum and use contains over it. For example:

Planets.values().map { it.name }.contains("EARTH")

But for this you need to have correct string value so you might uppercase it before search.

If you want to find enum by its value take a look at the Reverse Lookup for enums.

Edit:

As @JamesBassett suggested you can optimize it to stop looking once it finds a match.

Planets.values().any { it.name == "EARTH" }
Januson
  • 4,533
  • 34
  • 42
  • 11
    I'd probably simplify to `Planets.values().any { it.name == "EARTH" }` (you want it to stop looking once its found a match instead of looking through the whole list) – James Bassett Jan 25 '17 at 06:50
3

You could do something like this:

fun <T : Enum<*>> KClass<T>.contains(value: String): Boolean {
    return this.java.enumConstants.any { it.name == value }
}

MyEnum::class.contains("some string")
James Bassett
  • 9,458
  • 4
  • 35
  • 68
1

or if you are really adventurous

enum MyEnum {
    MY_1, MY_2;
   
    companion object {

        fun isInEnum(someString: String) {
            return try {
                valueOf(someString)
                true
            } catch (e: Exception) {
                false
            }
        }
    }
}