393

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
1
  • I too, come from the world of C# and had this same question; thank you for asking it! Commented Sep 5, 2020 at 21:03

2 Answers 2

685

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))
Sign up to request clarification or add additional context in comments.

11 Comments

In Kotlin 1.2.42 both solutions give the compile error: Cannot choose among the following candidates without completing type inference: public fun <T> compareBy(vararg selectors: (???) → Comparable<*>?): kotlin.Comparator<???> defined in kotlin.comparisons public fun <T> compareBy(vararg selectors: (T) → Comparable<*>?): kotlin.Comparator<T> /* = java.util.Comparator<T> */ defined in kotlin.comparisons
@arslancharyev31 This seems to be reported as a bug. It only shows up in the IDE; my gradle build succeeds.
This is sorting in ascending order, how to sort in descending order?
@Chandrika compareByDescending
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}
|
236

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 }))
    

6 Comments

Thanks, this is what I was looking for! I'm a bit new to kotlin, why do you need to have compareBy<Person> as opposed to just compareBy in your first bullet point?
@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<Person> { 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 }).
How to add Case Insensitive to that?
@KoustuvGanguly maybe this will help you stackoverflow.com/questions/52284130/…
here is an example of how to implement this try.kotlinlang.org/#/UserProjects/4qlomldkm9pvjac0b5vb8j78v0/…
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.