Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

sortedBy parameter as a variable

I have a method which needs to sort a collection in many possible ways. Instead of calling myCollection.sortedBy multiple times, I'd like to have the lambda which I'd pass to sortedBy in a variable, and later I'd pass that variable to one single call to sortedBy. However, I can't figure out the type which that lambda variable should have. Keep in mind that the "sorted-by fields" may have different types (but they're all Comparable, obviously).

Here's a simplified version of my problem:

data class Person(val name: String, val age: Int)

enum class SortBy {
    NAME, AGE
}

fun main(args: Array<String>) {
    val people = listOf(Person("John", 30), Person("Mary", 20))
    val sort = SortBy.NAME

    val comparator = when (sort) {
        SortBy.NAME -> { p: Person -> p.name }
        SortBy.AGE -> { p: Person -> p.age }
    }

    // the line below won't compile
    people.sortedBy(comparator).forEach(::println)
}

Any ideas?

like image 682
cd1 Avatar asked Jan 29 '23 05:01

cd1


1 Answers

The problem is that the two lambdas have very different types, so the type of the variable comparator is simply inferred to be Any, which is first common supertype. There is no other type (other than Any?) which this variable can have if you need to be able to assign either of the lambdas to it.

Then, depending on what you ended up with (either a (Person) -> Int or a (Person) -> String), the sortedBy method would have to somehow infer its type parameter, which again is impossible while handling both cases.


However, there's a solution. You can create Comparator<Person> instances in the different when branches instead which wrap the specific types of whatever the Person instances are compared by, with the compareBy function:

val comparator: Comparator<Person> = when (sort) {
    SortBy.NAME -> compareBy { it.name }
    SortBy.AGE -> compareBy { it.age }
}

And then use the sortedWith method that takes a Comparator instead of a selector lambda:

people.sortedWith(comparator).forEach(::println)
like image 68
zsmb13 Avatar answered Jan 30 '23 21:01

zsmb13