Why custom sorting
sorted() puts numbers from small to large and strings from A to Z. Often you need another order: the highest price first, the shortest word first, or names ordered by their last letter. For that, pass a lambda: a small function in braces with no name. It says what to sort each item by. In { it.length }, it is the one item the lambda gets, and the lambda returns that item's length.
The sorting functions
sortedBy { it.length }sorts by what the lambda returns, from small to large.sortedByDescending { it }sorts from large to small.sortedWith(compareBy({ it % 10 }, { it }))uses two rules. The second lambda only orders items that tie on the first.
val scores = mutableListOf(70, 95, 82)
val highFirst = scores.sortedByDescending { it }
val nums = mutableListOf(25, 13, 5, 41, 33)
val byLastDigit = nums.sortedWith(compareBy({ it % 10 }, { it }))
Printing the lists gives [95, 82, 70] and [41, 13, 33, 5, 25]. The first rule sorts by last digit; numbers with the same last digit (13 and 33, or 5 and 25) then go from small to large.
To send ties from large to small instead, build the comparison with compareBy<Int> { it % 10 }.thenByDescending { it }. Used with sortedWith on the same numbers, it gives [41, 33, 13, 25, 5], so 33 now comes before 13.
Things to know
- The
sorted...functions make a new list and leave the old one as it was. Withwordsholding[pear, fig, apple],words.sortedBy { it.length }gives[fig, pear, apple]whilewordsstill prints[pear, fig, apple]. - On a
mutableListOflist,sortByandsortWithsort the list itself instead. Afterwords.sortBy { it.length },wordsprints[fig, pear, apple]. - Don't chain
sortedBy { it % 10 }.sortedBy { it }to get two rules: the second call sorts everything again and forgets the first. On the numbers above it gives[5, 13, 25, 33, 41]. UsecompareBywith both lambdas.
The lesson's task
A pet shop has toy prices 12, 5, 30 and 8, and pets Rex, Luna, Bella, Max, Bo and Coco. Make highFirst with the prices from high to low, and byLastLetter with the names sorted by their last letter, with names that share a last letter going from A to Z.
- Call
sortedByDescendingonpriceswith{ it }: each price is sorted by itself, from the biggest down. - For the names, call
sortedWithwithcompareByand two lambdas. The first gives the last letter,{ it[it.length - 1] }. A string indexed with a number gives aChar, and chars sort in alphabetical order. - The second lambda is
{ it }, so names with the same last letter go from A to Z. Max and Rex both end in x, and this puts Max first.
class Solution {
fun mainFunction() {
val prices = mutableListOf(12, 5, 30, 8)
val names = mutableListOf("Rex", "Luna", "Bella", "Max", "Bo", "Coco")
val highFirst = prices.sortedByDescending { it }
for (price in highFirst) {
println(price)
}
val byLastLetter = names.sortedWith(compareBy({ it[it.length - 1] }, { it }))
for (name in byLastLetter) {
println(name)
}
}
}
The prices print as 30, 12, 8, 5. The names end in a (Bella, Luna), o (Bo, Coco) and x (Max, Rex), so they print as:
Bella
Luna
Bo
Coco
Max
Rex
Common mistakes
- Expecting the original list to change.
sortedByandsortedWithreturn a new list; print that one, notpricesornames. - Chaining two
sortedBycalls for two rules. Only the last one counts. - Mixing up the direction.
sortedBygoes small to large;sortedByDescendinggoes large to small.
Key takeaways
- A lambda tells
sortedByandsortedByDescendingwhat to sort each item by. sortedWith(compareBy(...))takes several lambdas; later ones only matter on ties.sorted...returns a new list, whilesortByandsortWithchange a mutable list in place.
