363

Let's say I have a list of People which I need to sort by Age first and then by Name.

Coming from a C#-background, I can easily achieve this in said language by using LINQ:

var list=new List<Person>();
list.Add(new Person(25, "Tom"));
list.Add(new Person(25, "Dave"));
list.Add(new Person(20, "Kate"));
list.Add(new Person(20, "Alice"));

//will produce: Alice, Kate, Dave, Tom
var sortedList=list.OrderBy(person => person.Age).ThenBy(person => person.Name).ToList(); 

How does one accomplish this using Kotlin?

This is what I tried (it's obviously wrong since the output of the first "sortedBy" clause gets overridden by the second one which results in a list sorted by Name only)

val sortedList = ArrayList(list.sortedBy { it.age }.sortedBy { it.name })) //wrong
marianosimone
  • 3,366
  • 26
  • 32
Fire095
  • 3,715
  • 2
  • 11
  • 10

2 Answers2

628

sortedWith + compareBy (taking a vararg of lambdas) do the trick:

val sortedList = list.sortedWith(compareBy({ it.age }, { it.name }))

You can also use the somewhat more succinct callable reference syntax:

val sortedList = list.sortedWith(compareBy(Person::age, Person::name))
Alexander Udalov
  • 31,429
  • 6
  • 80
  • 66
  • 1
    In Kotlin 1.2.42 both solutions give the compile error: `Cannot choose among the following candidates without completing type inference: public fun compareBy(vararg selectors: (???) → Comparable<*>?): kotlin.Comparator??> defined in kotlin.comparisons public fun compareBy(vararg selectors: (T) → Comparable<*>?): kotlin.Comparator /* = java.util.Comparator */ defined in kotlin.comparisons` – arslancharyev31 May 12 '18 at 16:45
  • 1
    @arslancharyev31 [This seems to be reported as a bug](https://youtrack.jetbrains.com/issue/KT-20406). It only shows up in the IDE; my `gradle build` succeeds. – Steven Jeuris May 23 '18 at 11:36
  • 7
    This is sorting in ascending order, how to sort in descending order? – Chandrika Jul 18 '18 at 07:58
  • 11
    @Chandrika `compareByDescending` – Alexander Udalov Jul 18 '18 at 12:32
  • 67
    `compareByDescending` would reverse all the functions. If you just wanna use one field for descending order, slap a `-` (minus) in front, like so `{-it.age}` – Abhijit Sarkar Jul 24 '18 at 03:13
  • What is "it" in the solution? Is it some sort of keyword? I cannot figure this out. Would be great if someone clarifies this. Thanks! – Alkis Mavridis Nov 17 '18 at 19:48
  • 1
    @Alkis Mavridis Please see https://kotlinlang.org/docs/reference/lambdas.html#it-implicit-name-of-a-single-parameter – Alexander Udalov Nov 18 '18 at 21:29
  • here is an example of how to implement this https://try.kotlinlang.org/#/UserProjects/4qlomldkm9pvjac0b5vb8j78v0/ev5h81c29ph8rmkf9udb7nb93j – epool Jul 05 '19 at 01:57
  • What if I want a bit more customized comparison, that requires more than the value, but really need the 2 values to compare by? – android developer Dec 08 '20 at 15:12
  • in addition to Abhijit Sarkar's answer. If field type is boolean, use `!` – Falchio May 20 '23 at 18:18
215

Use sortedWith to sort a list with Comparator.

You can then construct a comparator using several ways:

  • compareBy, thenBy construct the comparator in a chain of calls:

    list.sortedWith(compareBy<Person> { it.age }.thenBy { it.name }.thenBy { it.address })
    
  • compareBy has an overload which takes multiple functions:

    list.sortedWith(compareBy({ it.age }, { it.name }, { it.address }))
    
Miles Krell
  • 103
  • 1
  • 2
  • 8
hotkey
  • 140,743
  • 39
  • 371
  • 326
  • 1
    Thanks, this is what I was looking for! I'm a bit new to kotlin, why do you need to have `compareBy` as opposed to just `compareBy` in your first bullet point? – ElectronAnt Aug 04 '17 at 18:31
  • 2
    @Aneem, the Kotlin compiler is sometimes unable to infer the type argument, and that needs to be specified manually. One such case is when a generic type is expected, and you want to pass the result of generic functions calls chain, like `compareBy { it.age }.thenBy { it.name }.thenBy { it.address }`. In the second point, there's only one function call, no calls chaining: `compareBy({ it.age }, { it.name }, { it.address })`. – hotkey Aug 07 '17 at 15:54
  • 2
    How to add Case Insensitive to that? – K.Os Sep 11 '18 at 19:57
  • Have u got the solution? @Konrad – Koustuv Ganguly Oct 15 '18 at 11:37
  • 1
    @KoustuvGanguly maybe this will help you https://stackoverflow.com/questions/52284130/how-to-sort-list-of-objects-by-two-properties-and-collator – K.Os Oct 15 '18 at 20:08
  • here is an example of how to implement this https://try.kotlinlang.org/#/UserProjects/4qlomldkm9pvjac0b5vb8j78v0/ev5h81c29ph8rmkf9udb7nb93j – epool Jul 05 '19 at 01:57
  • @epool Your link shows an empty playground. – ASP Jan 04 '22 at 13:04