Problem overview
The function topScorer receives scores, a [String: Int] dictionary from players' names to scores. It returns the name of the player with the highest score. If several players share the highest score, it returns the one whose name comes first alphabetically. The dictionary always has at least one name, and every score is between 0 and 100.
For ["Ana": 90, "Ben": 75, "Cara": 88] the answer is "Ana". For ["Zoe": 82, "Ana": 82] both players have 82, so the answer is "Ana". For ["Max": 0] the answer is "Max".
Approach: keep the best player so far
You look at every entry once and remember the best one seen up to now. Each new player either beats the current best or doesn't. When the loop ends, the one remembered is the answer.
The one trap is the order. A Swift dictionary has no fixed order, and it can change between runs. A solution that returns "the first player with the top score" would give different answers on different runs. The tie rule fixes that: because the alphabetically first name wins, the answer is the same whatever order the loop uses.
A player beats the best so far in two cases: a higher score, or the same score and a name that comes earlier. In Swift, name < bestName on two strings is true when name sorts before bestName. All names here are a capital letter followed by lowercase ones, so the comparison matches alphabetical order.
Step by step
- Set up the best so far.
bestNamestarts as an empty string andbestScoreas -1, both withvar. Every real score is at least 0, so the first player visited always beats -1. - Loop over the entries.
for (name, score) in scoressplits each entry into its key and its value. - Test the player. The condition is a higher score, or an equal score with an earlier name. The tie check goes in parentheses so that it counts only when the scores are equal.
- Update the best. Inside the
if, store the name and score. - Return
bestNameafter the loop.
The solution in Swift
class Solution {
func topScorer(_ scores: [String: Int]) -> String {
var bestName = ""
var bestScore = -1
for (name, score) in scores {
if score > bestScore || (score == bestScore && name < bestName) {
bestName = name
bestScore = score
}
}
return bestName
}
}
Swift details worth knowing:
&&binds tighter than||, so the parentheses are not required by the compiler. They make the intent clear to a reader.- The loop needs the empty string and -1 as starting values because Swift has no "no value yet" for a plain
StringorInt. The -1 works because scores can't be negative. - The function returns a
String, not an optional, which is fine because the dictionary always has at least one name.
Tests
On the three examples the function returns:
Ana
Ana
Max
Another case: ["Ana": 5, "Ben": 5, "Al": 5] returns Al, since "Al" sorts before "Ana".
Common mistakes
- Starting
bestScoreat 0. A dictionary where everyone has 0, such as["Max": 0], would then never beat the start value, and the function would return an empty string. - Forgetting the tie rule. With only
score > bestScore, the winner of a tie is whichever entry the loop happens to reach first, so the result can change from run to run. - Dropping the parentheses logic.
score == bestScore && name < bestNamemust both be true. Checking the names alone would pick a lower-scoring player with an earlier name. - Returning inside the loop. The best player is only known after every entry has been compared.
