Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Convert Java Frequency Sort to Kotlin

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?

like image 685
Yahor Barkouski Avatar asked Jan 20 '26 17:01

Yahor Barkouski


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.

like image 138
Sweeper Avatar answered Jan 23 '26 05:01

Sweeper


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.

like image 29
thinkgruen Avatar answered Jan 23 '26 07:01

thinkgruen