Start learning free

LESSON 268 OF 674 · TUPLES, SORTING & BUILT-INS

Leaderboard in C#: Sorting Tuples with OrderByDescending

Practice Read the full explanation

You get a List 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 3
    Ctrl + Enter to check

    C# · TUPLES, SORTING & BUILT-INS

    Leaderboard in C#, explained

    Problem overview

    Leaderboard gets a List<(string Name, int Score)> named players, pairs like ("Ava", 30). It returns a List<string> with only the names, from the highest score to the lowest. Players with equal scores are ordered alphabetically by name. No two players share a name.

    For [("Mia", 50), ("Leo", 70), ("Ava", 50)] the result is ["Leo", "Ava", "Mia"]: Leo leads with 70, then Ava and Mia tie at 50 and Ava comes first.

    Approach: sort the pairs, then keep the names

    There are two jobs: ordering and picking out a part. Do them in that order, because the scores are needed to sort and are not needed afterwards.

    • OrderByDescending(p => p.Score) sorts from the biggest score to the smallest. The lambda receives one tuple, so p.Score reads its named part.
    • ThenBy(p => p.Name) orders players who tie on score from A to Z. Note the mix: descending for the score, ascending for the name.
    • Select(p => p.Name) turns each pair into just its name and keeps the order.

    Step by step

    1. Sort players with OrderByDescending on the score, then ThenBy on the name, then ToList(). Store it in ordered, declared with var.
    2. Select the names from ordered with Select and ToList(), and store them in a List<string> named names.
    3. Return names.

    The solution in C#

    class Solution {
        public List<string> Leaderboard(List<(string Name, int Score)> players) {
            var ordered = players.OrderByDescending(p => p.Score).ThenBy(p => p.Name).ToList();
            List<string> names = ordered.Select(p => p.Name).ToList();
            return names;
        }
    }
    

    With var, the compiler gives ordered the type List<(string Name, int Score)>, so p.Name and p.Score keep working in the next line. Writing that type out by hand would also be correct, just longer.

    Another way

    The two steps can be one chain, with no ordered variable:

    players.OrderByDescending(p => p.Score).ThenBy(p => p.Name).Select(p => p.Name).ToList()
    

    On [("Mia", 50), ("Leo", 70), ("Ava", 50)] this gives [Leo, Ava, Mia], the same as the lesson's code. The lesson splits it so each step can be checked on its own.

    You can also unpack the pairs in a foreach over the sorted sequence, foreach (var (name, score) in sorted). For that example it visits Leo 70, Ava 50 and Mia 50, in that order.

    Why the tie-break is a second key

    You might try to sort first by name and then by score, hoping the name order survives. OrderBy and OrderByDescending are stable, meaning tied items keep their earlier order, so that can work, but the code would have to run the sorts in the reverse order from the rule: name first, score last. ThenBy states the rule in the order you say it aloud ("by score, then by name") and avoids that trap. For example, sorting the same players with only OrderBy(p => p.Score) gives [Mia, Ava, Leo], with the tied Mia and Ava left in their input order, not alphabetical.

    Complexity

    The sort is O(n log n) for n players. Select adds one pass, O(n). The lists use O(n) extra space.

    Testing

    Outputs from running the solution:

    playersResult
    [("Ava", 30), ("Ben", 25), ("Cy", 40)][Cy, Ava, Ben]
    [("Mia", 50), ("Leo", 70), ("Ava", 50)][Leo, Ava, Mia]
    [("Zoe", 0)][Zoe]

    Common mistakes

    • Using OrderBy instead of OrderByDescending. That puts the lowest score first, the opposite of a leaderboard.
    • Using OrderByDescending twice. A second OrderByDescending on the name would put the ties in Z-to-A order. And a second sort call, instead of ThenBy or ThenByDescending, replaces the first sort's order rather than refining it.
    • Selecting before sorting. Once the pairs have become plain names, the scores are gone and there is nothing left to sort by.
    Keep going with C#500+ step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free