Search code examples
kotlincollectionsenumsgrouping

Exhaustive groupBy for enum keys


I would like to group a list to a Map that contains every single key of an enum class. Consider the following example:

enum class E { A, B, C }

fun main() {
    val l = listOf("Alice", "Anne", "Charlie")
    val a = l.groupBy { E.valueOf(it.substring(0,1)) }
    println(a)
}

As it is, groupBy returns {A=[Alice, Anne], C=[Charlie]}.

I would like it to return {A=[Alice, Anne], B=[], C=[Charlie]}.

I know I can create a function that iterates over all the entries of an enum and filters the list that way, thus creating what I want, but that's a very brute-force approach. Is there an existing function that does what I'm after, or a neat and concise way to do it?


Solution

  • One strategy would be to create a map of empty defaults and then fill it in using groupBy.

    val a2: Map<E, List<String>> = E.entries.associateWith { emptyList<String>() } + 
        l.groupBy { E.valueOf(it.substring(0,1)) }
    
    val a3: Map<Char, List<String>> = a2.mapKeys { (key, _) -> key.name.first() }
    

    This is using valueOf and I'm not sure of the time complexity of how it's implemented in the JVM so it may or may not be considered brute force itself. I suppose you could ensure it's not by creating a map first.

    val entriesByName = E.entries.associateBy(E::name)
    
    val a2: Map<E, List<String>> = E.entries.associateWith { emptyList<String>() } +
            l.groupBy { entriesByName.getValue(it.substring(0,1)) }