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?
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)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With