Start learning free

LESSON 269 OF 677 · PAIRS, SORTING & BUILT-INS

Leaderboard in Kotlin: Sorting Pairs by Score

Practice Read the full explanation

You get an ArrayList of players, players. Each player is a pair: a name and a score, like ("Ava", 30).

Return the names, from the highest score to the lowest.

Players with the same score go in alphabetical order of their names.

The starter code shows how your language holds a pair.

 

Example 1:

Input: players = [("Ava", 30), ("Ben", 25), ("Cy", 40)]
Output: ["Cy", "Ava", "Ben"]
Explanation: Cy has the most points, 40.
Then come Ava with 30 and Ben with 25.

Example 2:

Input: players = [("Mia", 50), ("Leo", 70), ("Ava", 50)]
Output: ["Leo", "Ava", "Mia"]
Explanation: Leo leads with 70.
Ava and Mia both have 50.
Ava comes first from A to Z.

Example 3:

Input: players = [("Zoe", 0)]
Output: ["Zoe"]
Explanation: Zoe is the only player.

 

Constraints:

  • players has 1 to 100 players.
  • Every score is a whole number from 0 to 1000.
  • Each name is a capital letter followed by lowercase letters, like "Ava".
  • No two players have the same name.
STEP 1 OF 5
    Ctrl + Enter to check

    KOTLIN · PAIRS, SORTING & BUILT-INS

    Leaderboard in Kotlin, explained

    Problem overview

    leaderboard receives a list of players. Each player is a Pair<String, Int>: a name and a score. It returns only the names, from the highest score to the lowest. Players with the same score go in alphabetical order of their names. Names are never repeated.

    For [("Ava", 30), ("Ben", 25), ("Cy", 40)] the answer is Cy, Ava, Ben. For [("Mia", 50), ("Leo", 70), ("Ava", 50)] Leo leads with 70, and Ava and Mia tie at 50, so the answer is Leo, Ava, Mia. A single player, [("Zoe", 0)], gives Zoe.

    Approach

    Sort the pairs first, then pull the names out in that order. The sort has two keys: the score from high to low, and for equal scores the name from A to Z.

    Kotlin's sortedWith(compareBy(...)) sorts by the lambdas in order. The lambdas sort ascending, and there is a trick for "highest first" with numbers: sort by the negative score. The score 40 becomes -40, which is smaller than -30, so Cy comes before Ava.

    Step by step

    1. Build ordered with players.sortedWith(compareBy({ -it.second }, { it.first })). In the lambdas it is a player: second is the score and first is the name.
    2. Make an empty list names with mutableListOf<String>().
    3. Loop over ordered with for (player in ordered).
    4. Inside the loop, add player.first to names.
    5. After the loop, return names.

    The solution in Kotlin

    class Solution {
        fun leaderboard(players: List<Pair<String, Int>>): List<String> {
            val ordered = players.sortedWith(compareBy({ -it.second }, { it.first }))
            val names = mutableListOf<String>()
            for (player in ordered) {
                names.add(player.first)
            }
            return names
        }
    }
    

    On the three examples it returns:

    [Cy, Ava, Ben]
    [Leo, Ava, Mia]
    [Zoe]
    

    The returned MutableList is accepted where a List<String> is expected, because a mutable list is also a read-only list.

    A run with a tie

    Take [("Ava", 30), ("Ben", 25), ("Cy", 40), ("Dee", 30)]. After the sort, ordered holds the whole pairs in this order:

    [(Cy, 40), (Ava, 30), (Dee, 30), (Ben, 25)]
    

    Cy has the highest score. Ava and Dee tie at 30, so the second lambda puts Ava before Dee. Ben is last. The loop then copies the names in that order, and the function returns [Cy, Ava, Dee, Ben]. The sorting does the thinking; the loop only strips the scores.

    The minus sign is a trick for numbers only. You can't negate a name, which is why the name is the second lambda and sorts in its normal A to Z order.

    Testing

    When every score is equal, only the names decide. For [("Mia", 5), ("Ava", 5), ("Leo", 5)] the answer is [Ava, Leo, Mia]. A list that is already in the right order, such as [("Cy", 40), ("Ava", 30), ("Ben", 25)], comes back as Cy, Ava, Ben. Sorting takes about n log n comparisons for n players, which is fine for at most 100.

    Another way

    You can sort in descending order directly with compareByDescending and chain the tie-break with thenBy:

    val ordered = players.sortedWith(compareByDescending<Pair<String, Int>> { it.second }.thenBy { it.first })
    

    On the second example it gives the same names, [Leo, Ava, Mia]. The negative score is shorter, and it works because scores are whole numbers in a small range.

    Edge cases and common mistakes

    • Only sorting by score. sortedByDescending { it.second } keeps tied players in their original order. On the second example it gives (Leo, 70), (Mia, 50), (Ava, 50), with Mia before Ava. The second lambda fixes this.
    • Dropping the minus sign. Then the lowest score comes first.
    • Returning pairs. The result type is List<String>, names only. Take player.first.
    • Swapping first and second. The name is the first slot, the score the second.
    Keep going with Kotlin500+ step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free