Convert Java Frequency Sort to Kotlin

Viewed 158

I need to sort List<String> by the number of occurrences in my list (DESC), and to remove repetitions from there. I wrote this working algorithm in java:

 private List<String> sortByFrequency(List<String> sequence) {
    return
            sequence.stream()
                    .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()))
                    .entrySet().stream()
                    .sorted(Map.Entry.<String, Long>comparingByValue(Comparator.reverseOrder())
                            .thenComparing(Map.Entry.comparingByKey()))
                    .map(Map.Entry::getKey)
                    .collect(Collectors.toList());
}

but it doesn't work for Kotlin: kotlin code

because I get the following exception:

    Type inference failed. Expected type mismatch: 
    required:
    Collector<in String!, Any!, Long!>!
    found:
    Collector<String!, *, Long!>!

and can't figure out how to resolve it. maybe can you tell me?

2 Answers

Let Kotlin figure out the types for you. Don't specify the type parameters unless you need to. Kotlin will try its best to find the correct types.

There is only one place that you need to specify the type parameters, and that is the Map.Entry.comparingByValue call.

This compiles:

fun sortByFrequency(sequence: List<String>): List<String?>? {
    return sequence.stream()
        .collect(Collectors.groupingBy({ it }, Collectors.counting()))
        .entries.stream()
        .sorted(
            Map.Entry.comparingByValue<String, Long>(Comparator.reverseOrder())
                .thenComparing(Map.Entry.comparingByKey())
        )
        .map { it.key }
        .collect(Collectors.toList())
}

However, the code still feels very Java-ish. IMO, this is more idiomatic Kotlin:

fun sortByFrequency(sequence: List<String>): List<String> {
    val comparator = compareByDescending<Map.Entry<String, Int>> { it.value }
        .thenBy { it.key }
    return sequence.groupingBy { it }.eachCount().entries
        .sortedWith(comparator).map { it.key }
}

Notice the use of groupingBy and eachCount.

The problem comes that * and Any are not meaning the same thing. They are treated as distinct types, so the compilation fails. In fact * can mean in Nothing or out Any? in Kotlin, depending on the context.

More info on the topic: https://kotlinlang.org/docs/generics.html#star-projections

In your case Kotlin should be able to infer the correct types for the collector for you, so can leave them out.

Related