Home > Mobile >  Grouping and ordering the top 3 values in a list
Grouping and ordering the top 3 values in a list

Time:04-07

I have a list similar in concept to the following:

val selectedValues = listOf("Apple", "Apple", "Apple", "Grape", "Grape", "Cherry")

I need a way to group and sort it so that I get something like this:

Apple: 3
Grape: 2
Cherry: 1

I got a little bit of headway with this answer but I want it to be ordered by the count (most to least) and I can't seem to figure out how to get there.

I feel like the answer I posted gets me very close to what I want but I just need to figure out how to get it to work the way I need to and I'm just hitting a wall and need a little assistance as I'm still fairly new to Kotlin.

CodePudding user response:

You could try something like this:

fun main(args : Array<String>) {
    val selectedValues = listOf("Apple", "Apple", "Apple", "Grape", "Grape", "Cherry")
    val solution = selectedValues
        .groupBy { it }
        .mapValues { it.value.size }
        .toList()
        .sortedByDescending { (_, value) -> value }
        .toMap()
    
   println(solution)
}

This will returns you

{Apple=3, Grape=2, Cherry=1}

which I think is (more or less) what you were after.

CodePudding user response:

If you want to get a bit clever, you can use a Grouping which allows you to do a group-and-fold operation, i.e. group a bunch of things and then turn each group into a result - there's an eachCount() operation which turns each group into a count:

selectedValues.groupingBy { it }.eachCount()
    .entries
    .sortedByDescending { it.value }
    .take(3) // they're still Entry pairs here, so you can toMap() if you need one
    .run(::println)

Stefano's approach is the most general (and definitely what I'd go for if you're still starting out), but there are some specific tools for some scenarios too (possibly with some performance optimisation going on under the hood)

There's also the "build your own map of counts" approach:

// this is basically the same as "val newMap, stuff.forEach { update count in newMap }"
selectedValues.fold(mutableMapOf<String, Int>()) { counts, item ->
        counts.apply { put(item, getOrDefault(item, 0)   1) }
    }

which I think is how the groupingBy().eachCount() combo above works - instead of creating a map of lists ("Apple"=["Apple", "Apple", "Apple"] and so on) and then generating a map of Ints from that, you just generate the Map<String, Int> from the original source list.

Whatever's easiest honestly, you only need to worry about efficiency when you're dealing with a lot of stuff, and Kotlin lets you write nice readable operations with its basic functions. Just thought I'd give you an idea of some of the options you have since you were struggling a little, in case it helps some things click!

  • Related