38

Kotlin allows me to sort ASC and array using multiple comparison fields.

For example:

return ArrayList(originalItems)
    .sortedWith(compareBy({ it.localHits }, { it.title }))

But when I try sort DESC (compareByDescending()), it does not allow me to use multiple comparison fields.

Is there any way I can do it?

earthw0rmjim
  • 19,027
  • 9
  • 49
  • 63
Augusto Carmo
  • 4,386
  • 2
  • 28
  • 61

4 Answers4

71

You could use the thenByDescending() (or thenBy() for ascending order) extension function to define a secondary Comparator.

Assuming originalItems are of SomeCustomObject, something like this should work:

return ArrayList(originalItems)
        .sortedWith(compareByDescending<SomeCustomObject> { it.localHits }
                .thenByDescending { it.title })

(of course you have to replace SomeCustomObject with your own type for the generic)

earthw0rmjim
  • 19,027
  • 9
  • 49
  • 63
  • Sweet! It works like a charm. Thank you very much, @earthw0rmjim! – Augusto Carmo Apr 03 '18 at 09:41
  • Here is an even more elegant solution: https://stackoverflow.com/questions/37259159/sort-collection-by-multiple-fields-in-kotlin, ```val sortedList = list.sortedWith(compareByDescending({ it.age }, { it.name }))``` – Gleichmut Jun 28 '23 at 04:53
9

You can also just use sort ASC and then reverse it:

return ArrayList(originalItems).sortedWith(compareBy({ it.localHits }, { it.title })).asReversed()

The implementation of the asReversed() is a view of the sorted list with reversed index and has better performance than using reverse()

leonardkraemer
  • 6,573
  • 1
  • 31
  • 54
6
ArrayList(originalItems)
    .sortedWith(compareByDescending({ it.localHits }, { it.title }))

You only need to define this function:

fun <T> compareByDescending(vararg selectors: (T) -> Comparable<*>?): Comparator<T> {
    return Comparator { b, a -> compareValuesBy(a, b, *selectors) }
}

Or you may use the Comparator directly:

ArrayList(originalItems)
    .sortedWith(Comparator { b, a -> compareValuesBy(a, b, { it.localHits }, { it.title }) })
JavierSA
  • 783
  • 1
  • 10
  • 25
1

Reversing the comparator also works:

originalItems.sortedWith(compareBy<Item>({ it.localHits }, { it.title }).reversed())
s1m0nw1
  • 76,759
  • 17
  • 167
  • 196