Start learning free

LESSON 272 OF 679 · TUPLES, SORTING & BUILT-INS

Leaderboard in Rust: Sorting Tuples by Score, Then Name

Practice Read the full explanation

You get a Vec 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

    RUST · TUPLES, SORTING & BUILT-INS

    Leaderboard in Rust, explained

    Problem overview

    The function leaderboard receives a vector of players. Each player is a tuple (String, i32): a name and a score. It returns only the names, from the highest score to the lowest. Players with equal scores go in alphabetical order of their names, and no two players share a name.

    For [("Ava", 30), ("Ben", 25), ("Cy", 40)] the answer is ["Cy", "Ava", "Ben"]. For [("Mia", 50), ("Leo", 70), ("Ava", 50)] it is ["Leo", "Ava", "Mia"]: Leo leads with 70, and Ava and Mia tie at 50, so Ava comes first.

    Approach

    Sorting by score ascending and then reversing would break the tie rule, because reversing the whole list also reverses the order of players with equal scores, so they would no longer be A to Z. Comparing the scores in the other direction avoids that.

    Sort the pairs first, then pull out the names. The sort has two rules, and sort_by with a chained then expresses both: compare the scores in descending order, and use the names only when the scores are Equal.

    To sort from high to low, swap the two sides of cmp: b.1.cmp(&a.1) instead of a.1.cmp(&b.1). For the names, the normal order a.0.cmp(&b.0) gives A to Z.

    Step by step

    1. Sort the vector in place. players is declared mut. In the closure, .1 is the score and .0 is the name of each tuple.
    2. Score rule: b.1.cmp(&a.1). Comparing b with a puts the highest score first.
    3. Name rule: chain .then(a.0.cmp(&b.0)), which only counts when the scores are equal.
    4. Create an empty vector names with Vec::new(), declared let mut. Rust infers its type from the push calls and the return type.
    5. Loop over players itself, not &players. The loop takes each tuple out of the vector, so its name can be moved into names without copying.
    6. Push player.0 onto names. The scores are left behind.
    7. Return names after the loop.

    The solution in Rust

    impl Solution {
        fn leaderboard(mut players: Vec<(String, i32)>) -> Vec<String> {
            players.sort_by(|a, b| b.1.cmp(&a.1).then(a.0.cmp(&b.0)));
            let mut names = Vec::new();
            for player in players {
                names.push(player.0);
            }
            return names;
        }
    }

    Because the loop consumes players, the vector can't be used after it. That is fine here, since the function no longer needs it. Looping over &players instead would need player.0.clone() to get an owned name.

    Another way

    A tuple of keys can do the same with std::cmp::Reverse, which flips the order of one field:

    players.sort_by_key(|p| (Reverse(p.1), p.0.clone()));

    Given [("Mia", 50), ("Leo", 70), ("Ava", 50)], this gives [("Leo", 70), ("Ava", 50), ("Mia", 50)] (with use std::cmp::Reverse; in a file of your own). The sort_by closure from the lesson says the same thing without an import or a clone.

    Testing

    The three examples give:

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

    Edge cases and common mistakes

    • Sorting scores ascending. Writing a.1.cmp(&b.1) puts the lowest score first.
    • Flipping the name rule too. The names must stay A to Z, so only the score comparison is swapped.
    • Looping over &players and pushing player.0. This tries to move a String out of a borrowed tuple, and the compiler reports that it cannot move out of a shared reference. Use player.0.clone() there.
    • Returning the pairs. The function returns only names. Sorting the pairs is a step on the way, not the answer.
    Keep going with Rust500+ step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free