Problem overview
The method topScorer gets scores, an object that maps each player's name to a score, and returns the name of the player with the highest score. If several players share the highest score, it returns the name that comes first alphabetically. Scores are whole numbers from 0 to 100, and there is at least one player.
For {"Ana": 90, "Ben": 75, "Cara": 88} the answer is "Ana". For {"Zoe": 82, "Ana": 82} both have 82, so the answer is "Ana", which comes before "Zoe". For {"Max": 0} the only player wins, even with a score of 0.
Approach
Keep the best player seen so far, and look at every player once. Each player either beats the current best or doesn't. A player beats it when the score is higher, or when the score is equal and the name comes earlier alphabetically.
The tie rule matters because the order in which a loop visits an object is not something to rely on. If the code only checked "higher score", a tie would be won by whoever the loop visits first, and the result would depend on how the object was written. Comparing names on a tie makes the answer the same for any order.
Step by step
- Set up the best so far:
bestNameis an empty string andbestScoreis -1, both withletbecause they change. Every real score is at least 0, so the first player always beats -1. - Loop over the keys with
for (const name in scores).for...ingives the keys of an object, one at a time. - Read the score of the current key into a constant:
scores[name]. - Check whether this player wins: a higher score, or an equal score and a name that comes first. The tie check pairs "equal score" with "earlier name", so a lower score never wins on its name alone. Parentheses make that grouping easy to read.
- Update the best name and score inside the
if. - Return
bestNameafter the loop, once everyone has been compared.
The solution in JavaScript
class Solution {
topScorer(scores) {
let bestName = "";
let bestScore = -1;
for (const name in scores) {
const score = scores[name];
if (score > bestScore || (score == bestScore && name < bestName)) {
bestName = name;
bestScore = score;
}
}
return bestName;
}
}
Two details are worth knowing:
- Comparing strings with
<. On two strings,<compares them character by character in dictionary order, as long as the case matches."Ana" < "Zoe"istrueand"Zoe" < "Ana"isfalse. All names here are a capital letter followed by lowercase letters, so the comparison behaves the way you expect. A lowercase"ana"would sort after"Zoe", because every capital letter comes before every lowercase letter. ==on numbers. Both sides are numbers, so==works. Many people write===out of habit, which gives the same result here.
Testing
Running the method on the statement's examples gives:
Ana
Ana
Max
The tie case gives Ana whichever way the object is written: {"Zoe": 82, "Ana": 82} and {"Ana": 82, "Zoe": 82} both return Ana.
Common mistakes
- Skipping the tie rule. With only
score > bestScore, the first player visited keeps a tie. The same code returnsZoefor{"Zoe": 82, "Ana": 82}andAnafor{"Ana": 82, "Zoe": 82}. - Starting
bestScoreat 0. Then a lone player with a score of 0, like Max, never beats it, and the function returns an empty string. Starting at -1 avoids that. - Returning the score instead of the name. The loop tracks both; the function returns
bestName.
