Problem overview
countVote receives votes, an object from names to how many votes each person has, and the name on a new vote. The person called name gets one more vote, and the function returns votes. A name with no votes yet isn't a key. It starts from 0, so after its first vote it has 1. The order of the entries doesn't matter.
With {"Ana": 3, "Ben": 5} and "Ana", Ana goes from 3 to 4. With "Cara", who isn't a key, she is added with 1. With an empty object and "Max", the result is {"Max": 1}.
Approach
The difficulty is the person with no votes. Reading a missing key does not give 0. It gives undefined, and undefined + 1 is NaN. So start a local count at 0, and replace it with the stored count only when the key exists. Then one assignment, count + 1, handles both cases.
Step by step
- Start the count at 0. A
numbervariable namedcount, declared withletbecause it may change. - Read the old count if there is one. If
name in votes, setcounttovotes[name]. A missing name leaves the 0. - Store the new count.
votes[name] = count + 1. For a new name this adds the key with 1, and for an existing one it replaces the old count. - Return
votes. Only the entry ofnamechanged.
The solution in TypeScript
class Solution {
countVote(votes: { [key: string]: number }, name: string): { [key: string]: number } {
let count: number = 0;
if (name in votes) {
count = votes[name];
}
votes[name] = count + 1;
return votes;
}
}
The three examples print:
{ Ana: 4, Ben: 5 }
{ Ana: 3, Ben: 5, Cara: 1 }
{ Max: 1 }
Tracing the second example
For {"Ana": 3, "Ben": 5} and "Cara", count starts at 0. "Cara" in votes is false, so the if body is skipped and count is still 0. The assignment then stores 0 + 1 = 1 under "Cara", which creates the key. For "Ana" the if body runs first and sets count to 3, so the same assignment stores 3 + 1 = 4.
That is the value of the pattern: one assignment at the end serves both the new name and the existing one, and only the starting value differs.
Why check with in and not with the value?
A tempting shortcut is if (votes[name]). It happens to work here, because the constraints say every stored count is at least 1, so a stored count is never falsy. It would break on a dictionary that can hold 0. The in check asks the exact question, "is this a key?", and stays correct whatever values the dictionary holds.
Edge cases and common mistakes
- The first vote. Without the default,
votes["Max"] += 1on an empty object leaves{ Max: NaN }. The type says the value is anumber, but types are erased when the code is compiled, so the compiler does not notice. - Overwriting with 1. Writing
votes[name] = 1unconditionally would reset Ana to 1. - Declaring
countafter theif. It would replace the value just read. The default goes first. - Using
constforcount. Aconstcannot be reassigned inside theif, so uselet. - Short form.
votes[name] = (votes[name] ?? 0) + 1does the same job in one line: it gives{ Ana: 4, Ben: 5 },{ Ana: 3, Cara: 1 }and{ Max: 1 }for names that exist, are new, or start an empty object. The lesson spells the default out so that each step is visible.
