Initialize an EnumMap from all of enum's values
Asked Answered
P

3

7

I have an enum class:

enum class E { A, B, C, D }

What is the neatest way to initialize an EnumMap containing all of E's values as keys, each with an initial value of 0?

val map = ...?
assert(map is EnumMap<E, Int>)
assert(map[E.A] == 0)
assert(map[E.B] == 0)
assert(map[E.C] == 0)
assert(map[E.D] == 0)

The most concise I could think of is:

val map = E.values().associateWithTo(EnumMap(E::class.java)) { 0 }

However, the repetition of the name E breaks the DRY principle. And the word associateWithTo is a bit of a mouthful. Is there a more concise and readable way? I wish there was something like EnumMap.allOf() just like there is EnumSet.allOf().

Penetralia answered 25/11, 2021 at 21:38 Comment(2)
Do you really need it to be an EnumMap? Map isn't enough?Shortwinded
This map will be used hundreds of thousands of times a second. An enumMap would be equivalent to an array index. Other maps would be less efficient: hashmap would look up the hash code of the enum value and go to a table that contains a linked list.Penetralia
S
14

The simplest way that I can think of is:

val map = EnumMap(E.values().associateWith { 0 })
Sidesman answered 25/11, 2021 at 21:54 Comment(5)
Yeah this is as basic as it gets I think, with no repetition and no need to specify types explicitly. You could chain it like E.values().associateWith { 0 }.run(::EnumMap) if you like. And you can use enumValues<E>() if that's your thing, like @broot is doing in the utility function idea. But do you need one? If you're doing this a lot then maybe, but this is pretty straightforward and readable I think!Clypeate
Thanks for the hints @cactustictacs. I also prefer it as is simply because it seems more readable, but I would say we are entering the realm of personal preferences ;)Shortwinded
Oh those comments were aimed at the OP, not you! Since they're shopping for possibilities and allClypeate
Yup, I understood. Still, they were also good hints to me of other possible ways to do it. It is always good to have different perspectives on the same problem ;)Shortwinded
Oh right, no worries! And yeah, I like pointing out the chaining thing because that functional pipeline approach can be a bit more readable sometimes, I think. Even though this one's pretty short, it's nice to be able to read it left-to-right as E -> get its values -> make a map with each one set to 0 -> turn that into an EnumMap. Not necessarily better here, but like you said, personal preference!Clypeate
S
4
val map = EnumMap(EnumSet.allOf(E::class.java).associateWith { 0 })
// {A=0, B=0, C=0, D=0}

Or if you want the map with the 'ordinal' (the position in the enum):

val map = EnumMap(EnumSet.allOf(E::class.java).associateWith(E::ordinal))
// {A=0, B=1, C=2, D=3}

This would be the version for enums with custom properties:

enum class E(val value: String) {
  A("AAA"),
  B("BBB"),
  C("CCC"),
  D("DDD")
}

val map = EnumMap(EnumSet.allOf(E::class.java).associateWith(E::value))
// {A=AAA, B=BBB, C=CCC, D=DDD}
Skeleton answered 25/11, 2021 at 22:3 Comment(0)
C
3

You can always create your own utility function for this purpose:

fun main() {
    val map = enumMap<E, Int> { 0 }
}

inline fun <reified E : Enum<E>, V> enumMap(valueSelector: (E) -> V): EnumMap<E, V> {
    return enumValues<E>().associateWithTo(EnumMap(E::class.java), valueSelector)
}
Cosby answered 25/11, 2021 at 21:55 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.