Sorting Collections by Multiple Fields in Kotlin: 5 Fatal Mistakes

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Why Does "Simple" Sorting in Kotlin Break Code?

\n

Hello, colleague! If you've ever written in Kotlin and tried to sort a list of tasks first by date and then by priority, you've probably encountered a situation where the result looks like a random set. You're not alone — the question "Sort collection by multiple fields in Kotlin" has gathered thousands of views on Stack Overflow. It seems simple, right? But the devil, as always, is in the details.

\n

In this article, we'll break down the top mistakes when sorting by multiple fields in Kotlin that even experienced developers make. You'll learn how sortedWith works, why compareBy and thenBy are your best friends, and how not to lose data due to null. Let's go!

\n\n

Mistake #1: Sorting Like in Java — Using Comparator Manually

\n

Kotlin is not Java. But many coming from the Java world write:

\n
val list = listOf(\n    User("Alice", 25),\n    User("Bob", 20),\n    User("Charlie", 25)\n)\n\nval sorted = list.sortedWith(Comparator { a, b ->\n    val ageCompare = a.age.compareTo(b.age)\n    if (ageCompare != 0) ageCompare else a.name.compareTo(b.name)\n})\n
\n

Yes, it works. But it's terribly unreadable, and you can easily get lost in chains of if-else. Kotlin offers an elegant way — comparator functions.

\n

How to do it correctly?

\n
val sorted = list.sortedWith(compareBy<User> { it.age }.thenBy { it.name })\n
\n

Short, clear, no extra logic. compareBy creates the primary comparator, and thenBy adds secondary fields. This is sorting by multiple fields in Kotlin in its ideal form.

\n\n

Mistake #2: Ignoring null When Sorting

\n

Let's say we have a list of tasks where deadline can be null. If you simply write:

\n
tasks.sortedBy { it.deadline }\n
\n

…you'll get a NullPointerException. Kotlin is strict about null, and that's great. But what if null is normal?

\n

Solution: nullsFirst / nullsLast

\n

Use special wrappers:

\n
val sorted = tasks.sortedWith(\n    compareBy<Task>(nullsLast()) { it.deadline }\n        .thenBy { it.priority }\n)\n
\n

Here, nullsLast() places all null dates at the end of the list. There's also nullsFirst() — the opposite. This solves the problem, and your Kotlin collection sorting becomes safe.

\n\n

Mistake #3: Confusion Between sortedBy and sortedWith

\n

Many beginners write:

\n
list.sortedBy { it.age }.sortedBy { it.name }\n
\n

And are surprised that sorting by name "overrides" the sorting by age. sortedBy creates a new list each time and sorts only by one field. The last call is the main one.

\n

How to fix it?

\n

Use sortedWith with a compareBy chain:

\n
list.sortedWith(compareBy({ it.age }, { it.name }))\n
\n

Or a multi-line option for complex logic:

\n
list.sortedWith(compareBy<Person> { it.age }.thenByDescending { it.name })\n
\n

Remember: one call to sortedWith — one sort by multiple fields.

\n\n

Mistake #4: Sorting in Reverse Order — in the Wrong Place

\n

Sometimes you need to sort by age ascending, but by name descending. A typical mistake:

\n
list.sortedByDescending { it.age }.sortedBy { it.name }\n
\n

Again, the same problem — the second sort breaks the first. The correct approach:

\n
val sorted = list.sortedWith(\n    compareBy<Person> { it.age }\n        .thenByDescending { it.name }\n)\n
\n

Or for full control:

\n
val sorted = list.sortedWith(\n    compareBy(Person::age).thenByDescending(Person::name)\n)\n<                

Blogs

Book Recommendations