Problem overview
The function leaderboard receives a slice of players, players. Each one is a Player struct, declared for you, with a Name and a Score. It returns only the names, highest score first. Players with the same score go in alphabetical order by name, and no two players share a name.
For Ava (30), Ben (25) and Cy (40) the result is Cy Ava Ben. For Mia (50), Leo (70) and Ava (50) it is Leo Ava Mia: Leo leads, and Ava comes before Mia on the 50-point tie.
Approach: sort the players, then collect the names
The job has two parts. First put the players in the right order, then copy their names into a new slice in that order. Sorting the players, not the names, is the key: the scores are needed to decide the order, and the names alone would lose them.
sort.Slice takes the slice and a function of two indexes, i and j, that returns true when player i goes before player j. There are two rules, and the second only counts on a tie:
- If the scores differ, the higher score goes first.
- If the scores are equal, the name that comes first from A to Z goes first.
Step by step
- Sort with
sort.Slice. Ifplayers[i].Scoreandplayers[j].Scorediffer, returnplayers[i].Score > players[j].Score. The>is what puts high scores first. - Break ties with a last line,
return players[i].Name < players[j].Name. Two strings compare with<in dictionary order. - Create
namesas an empty slice of strings, withnames := []string{}. - Loop over the sorted players with
for _, player := range players. - Append each name:
names = append(names, player.Name).appendmay return a different slice, so the result is assigned back tonames. - Return
namesafter the loop.
The solution in Go
func leaderboard(players []Player) []string {
sort.Slice(players, func(i, j int) bool {
if players[i].Score != players[j].Score {
return players[i].Score > players[j].Score
}
return players[i].Name < players[j].Name
})
names := []string{}
for _, player := range players {
names = append(names, player.Name)
}
return names
}
The lesson declares the struct as type Player struct { Name string; Score int }. The comparison function reads the players through players[i] and players[j], because sort.Slice hands it indexes, not items. Since names are unique, no two players tie on both rules, so the order is fully decided even though sort.Slice is not stable.
Writing []string{} rather than var names []string returns an empty, non-nil slice if there were no players. Both print as []. The sort also reorders the caller's slice, since slices share their backing array.
Testing
Running the function on the statement's examples prints:
[Cy Ava Ben]
[Leo Ava Mia]
[Zoe]
Edge cases and common mistakes
- Ascending scores. Using
<on the scores puts the lowest score first. - Descending names. The tie rule uses
<on names: A to Z, even though the scores go high to low. - Forgetting to assign
append. Callingappend(names, player.Name)alone is a compile error in Go (its result is unused), and an oldnameswould stay short anyway. - Sorting a names slice only. The scores would no longer be available to compare.
- Returning the players. The function returns
[]string, so the loop copies out only the names. - A single player. The sort has nothing to do, and the loop returns that one name.
