Problem overview
leaderboard(players) receives an array of players. Each player is a two-item array, [name, score], such as ["Ava", 30]. The function returns just the names, from the highest score to the lowest. Players with the same score go in alphabetical order of their names.
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
There are two jobs: put the players in order, then keep only the names. The order comes from sort with a compare function that has two rules, like the previous sorting exercises:
- If the scores differ, the higher score goes first. For a descending order, return
b[1] - a[1]. It is negative whenahas more points, soagoes first. - If the scores are equal, compare the names with
a[0].localeCompare(b[0]).
Writing a[1] - b[1] instead would give lowest first, so the order of a and b in the subtraction is what flips the direction.
Step by step
- Call
players.sortwith a compare function ofaandb. Its first line returnsb[1] - a[1]when the scores differ. - Below that line, return
a[0].localeCompare(b[0])for equal scores. - Start an empty array,
names. - Loop over the sorted players with
for (const [name, score] of players). The square brackets unpack each pair intonameandscore. - Inside the loop, push
nameontonames. - After the loop, return
names.
The solution in JavaScript
class Solution {
leaderboard(players) {
players.sort((a, b) => {
if (a[1] !== b[1]) {
return b[1] - a[1];
}
return a[0].localeCompare(b[0]);
});
let names = [];
for (const [name, score] of players) {
names.push(name);
}
return names;
}
}
The loop never uses score, but the pair has two parts, so the destructuring names both. By then the players are sorted, so pushing the names one by one gives them in leaderboard order. The three examples return:
["Cy","Ava","Ben"]
["Leo","Ava","Mia"]
["Zoe"]
Sorting changes the array that was passed in. After the call on the second example, players itself is [["Leo",70],["Ava",50],["Mia",50]].
Reading the compare function
The function decides only which of two players goes first. Trying it on a few pairs from the second example gives these results:
compare(["Leo",70], ["Mia",50]) -> -20 (Leo goes first)
compare(["Mia",50], ["Leo",70]) -> 20 (Leo goes first)
compare(["Mia",50], ["Ava",50]) -> 1 (same score, Ava goes first)
Only the sign matters. A negative number keeps a before b, and a positive number puts b first.
Another way to collect the names
The loop can be replaced by map. After the sort, return players.map(([name]) => name); gives the same names. The lesson uses the loop so the unpacking in for (const [name, score] of players) gets practice.
Why two rules in one function
A single compare function can express a "sort by this, then by that" order by checking the main key first and falling back to the second key only when the first key is equal. That is exactly what the if does here: it returns early whenever the scores differ, so the name comparison only runs for players who tie. The names are all different, so the second rule always gives a clear order and never needs a third.
Edge cases and common mistakes
- Wrong direction.
a[1] - b[1]sorts lowest first. On the first example it gives["Ben", "Ava", "Cy"]. - Missing tie-break. Sorting by score alone on the second example gives
["Leo", "Mia", "Ava"], because Mia was before Ava in the input and ties keep their input order. The tests expect Ava first. - Returning pairs. The function returns only names.
- One player. A single pair like
["Zoe", 0]sorts to itself and gives["Zoe"].
