2

Is there any correct way to get top N elements from SortedMap? My variant is:

val sortedMap = map.filterValues { it in sortedValues }.toSortedMap()
if (sortedMap.size <= 20) {
    return sortedMap
}
var result = mutableMapOf<String, Int>()
for ((key, value) in sortedMap) {
    result[key] = value
    if (result.size == 20) {
        break
    }
}
Galina Melnik
  • 1,947
  • 3
  • 14
  • 19

4 Answers4

3

headMap is also a good solution for getting a portion of this map, for example

      return when {
        sortedMap.size >= 20 -> {
            sortedMap.headMap(sortedMap.keys.elementAt(20))
        }
        else -> {
            sortedMap
        }
    }

more from the docs here

Ben Shmuel
  • 1,819
  • 1
  • 11
  • 20
2
val first20AsList: List<Map.Entry<String, Int>> = sortedMap.asIterable().take(20)
val first20AsMap: Map<String, Int> = first20AsList.associate { it.toPair() }
2

Solution

In my opinion the best way is to use asSequence() because it is evaluated lazily:

return sortedMap.asSequence().take(20).map{ it.toPair() }.toMap()

Information on Lazy evaluation

In general it is often favorable to use lazy variants of iterable containers, because it means that in many cases not the whole datastructure needs to be evaluated but just the portion of data that is required.

In your case that would be the first N in the SortedMap. If it really happens to result in a performance advantage is questionable, but at least it is possible.

Some Information from stack-overflow and kotlin on lazy sequences:

Old Answer:

This answer was improved in some details thanks to the comment by @IR42. Before I used the spread operator for conversion to a sorted-map again (via sortedMapOf), and map { it.key to it.value } instead of { it.toPair() }:

return sortedMapOf(
    *sortedMap.asSequence().take(20)
         .map{ it.key to it.value }
         .toList().toTypedArray())
Jan
  • 2,025
  • 17
  • 27
  • 1
    `.take(20).map { it.toPair() }.toMap()` – IR42 Oct 21 '20 at 09:56
  • thanks, i will edit it accordingly, Ok so it will automagically create a sortedMap again? – Jan Oct 21 '20 at 09:57
  • no, but according to the code in the question, it doesn't matter which "map" is under the hood, most likely the return type of the function is `Map`. You also can use `.toMap(sortedMapOf())` if you need `SortedMap` – IR42 Oct 21 '20 at 10:07
  • you are right, the OP just used the sortedMap to get the first 20 elements and returns a standard mutable-map afterwards. Thanks for pointing that out as well. :D Actually I was just thinking it would be great to have some notion of *sharing* points/reputation if an answer was much improved by e.g. a comment or anything. – Jan Oct 21 '20 at 10:11
0

What u could try is flatMap{} from where u can get list of those items and then use take() function on that list to determine amount of it. Example:

val list = mapOf<Int, Boolean>(
    1 to true,
    2 to false,
    3 to true
).toSortedMap().flatMap {
    listOf(it)
}.take(2) 
println(list[1].key) // <-- 2

take(n)

Returns a list containing first n elements.

P.Juni
  • 2,365
  • 14
  • 26