I'd like to sort a List using a function reference...
# getting-started
d
I'd like to sort a List using a function reference looked up from a Map. If all function references returned the same type (
String
, for example) this would work fine. However in the case below where the return type is
Any
, it doesn't work.
Copy code
data class Foo(val name:String, val age:Int)        
        val sortModes = mapOf("name" to Foo::name, "age" to Foo::age)
        val myList = listOf(Foo("Dog", 17), Foo("Cat", 12), Foo("Mouse", 3))
        val sortFunc = sortModes["name"] ?: error("Not found") //  returns KProperty1<Foo, Any>
        println(myList.sortedWith(compareBy(sortFunc)) // <-- compiler error
What magic is required to get Kotlin to sort by a function reference that can return
Any
?
This works, but yuk:
Copy code
println(myList.sortedWith(compareBy {
            val value = sortFunc(it)
            when(value) {
                is String -> value
                is Int -> value
                else -> error("unsupported type")
            }
        }))
h
Could you sort from a Comparator looked up from a map instead? Something like:
Copy code
data class Foo(val name:String, val age:Int)
    val sortModes = mapOf("name" to Comparator { o1, o2 -> o1.name.compareTo(o2.name) }, 
                                          "age" to Comparator<Foo> { o1, o2 -> o1.age - o2.age })
    val myList = listOf(Foo("Dog", 17), Foo("Cat", 12), Foo("Mouse", 3))
    val sortFunc = sortModes["name"] ?: error("Not found")
    println(myList.sortedWith(sortFunc))
d
Thanks -- that does work, but I still feel like there must be an even simpler way, I'm just struggling to figure out what that would be.
Here's a slight improvement based on Brian's idea:
Copy code
val sortModes = mapOf(
            "name" to compareBy(Foo::name),
            "age"  to compareBy(Foo::age)
        )
        val myList = listOf(Foo("Dog", 17), Foo("Cat", 12), Foo("Mouse", 3))

        val sortFunc = sortModes["age"] ?: error("Not found")
        println("by name: ${myList.sortedWith(sortFunc)}")
I'm going with that unless anyone has any other suggestions.
👍 1
h
I knew there was something like
compareBy
but couldnt think of it
I think that's an ideal solution really, you end up with a Map<String, Comparator<Foo>> which is much nicer than Map<String, Any>
d
Agreed. Thanks again for your help!