Start learning free

LESSON 249 OF 569 · FUNCTIONS & CLASSES

Score Keeper Class in JavaScript

Practice O(1) Read the full explanation

A game shows each player two numbers: their total, all the points of their turns added up, and their best single turn. Write a class ScoreKeeper that keeps both numbers up to date as the points come in, and can start over.

Implement the class ScoreKeeper:

  • ScoreKeeper() creates a keeper with no points yet.
  • add(points) records the points of one turn.
  • total() returns the sum of all the points added since the keeper was created or last reset.
  • best() returns the highest points of a single add in that time, or 0 if there was no add yet.
  • reset() starts over: afterwards total() and best() both return 0 until the next add.

The input is a list of operations, applied in order to one new ScoreKeeper. The output lists what the total and best calls return, in order. The starter code spells these names the way your language does (in Go and Rust, ScoreKeeper is a struct with methods). Give the fields names that differ from the methods' names: in several languages a field named total would clash with the method total().

 

Example 1:

Input:
    add(10)
    add(25)
    add(5)
    total()
    best()
Output: 40 25
Explanation: 10 + 25 + 5 = 40. The best single turn was the 25.

Example 2:

Input:
    best()
    total()
    add(0)
    best()
    total()
Output: 0 0 0 0
Explanation: Before any add, both numbers are 0. A turn worth 0 points changes neither.

Example 3:

Input:
    add(30)
    add(50)
    total()
    best()
    reset()
    total()
    best()
    add(20)
    best()
    total()
Output: 80 50 0 0 20 20
Explanation: After reset() the keeper starts over. The 50 from before no longer counts,
so after add(20) the best is 20, not 50.

 

Constraints:

  • 0 <= points <= 1000
  • At most 300 operations on one ScoreKeeper.

Note:

Every method should run in O(1) time: add updates the two numbers as each turn comes in, so total and best never need to look back at old turns.

STEP 1 OF 7
    Ctrl + Enter to check

    JAVASCRIPT · FUNCTIONS & CLASSES

    Score Keeper in JavaScript, explained

    Problem overview

    A game shows each player two numbers: the total of all their turns and their best single turn. ScoreKeeper keeps both up to date. Its methods are add(points) to record a turn, total() and best() to report the numbers, and reset() to start over.

    After add(10), add(25) and add(5), the total is 40 and the best is 25. A new keeper, before any add, reports 0 for both. After reset() the old turns no longer count: with a best of 50, a reset and then add(20), the best is 20, not 50.

    Approach

    The keeper never needs the list of turns. It is enough to keep two numbers and update them as each turn arrives: a running sum, and the highest value seen so far. That is the same idea as finding the largest value in a list, one value at a time. Because nothing is looked up or re-added, every method runs in O(1) time.

    Points are never negative (the constraint is 0 to 1000), so 0 is a safe starting value for the best: any turn is at least as large.

    Step by step

    1. Two fields. In the constructor, set this.totalPoints and this.bestPoints to 0.
    2. Choose the names with care. The fields can't be called total and best: a property set in the constructor lives on the object itself, so it hides the method of the same name.
    3. Add. Add points to this.totalPoints with +=. Then, if points is greater than this.bestPoints, store it as the new best.
    4. Report. total() returns this.totalPoints and best() returns this.bestPoints. They only read what add has kept.
    5. Reset. Set both fields back to 0, the values a new keeper starts with.

    The solution in JavaScript

    class ScoreKeeper {
        constructor() {
            this.totalPoints = 0;
            this.bestPoints = 0;
        }
    
        add(points) {
            this.totalPoints += points;
            if (points > this.bestPoints) {
                this.bestPoints = points;
            }
        }
    
        total() {
            return this.totalPoints;
        }
    
        best() {
            return this.bestPoints;
        }
    
        reset() {
            this.totalPoints = 0;
            this.bestPoints = 0;
        }
    }
    

    Using the class

    let k = new ScoreKeeper();
    k.add(10);
    k.add(25);
    k.add(5);
    console.log(k.total(), k.best());
    
    k = new ScoreKeeper();
    console.log(k.best(), k.total());
    k.add(0);
    console.log(k.best(), k.total());
    
    k = new ScoreKeeper();
    k.add(30);
    k.add(50);
    console.log(k.total(), k.best());
    k.reset();
    console.log(k.total(), k.best());
    k.add(20);
    console.log(k.best(), k.total());
    

    This prints:

    40 25
    0 0
    0 0
    80 50
    0 0
    20 20
    

    Common mistakes

    • A field named like a method. With this.total = 0 in the constructor and a method total() in the class, calling keeper.total() fails with TypeError: keeper.total is not a function, because the property holding the number is found first.
    • Forgetting to reset the best. Resetting only the total leaves the old best in place, so after reset() and add(20) the best would still be 50.
    • Starting the best too high or too low. Starting it at 0 works because points are never negative. Without an add, best() has to return 0.
    • Overwriting the total. this.totalPoints = points keeps only the last turn; the total needs +=.

    Why not keep every turn?

    A first idea is to push each turn into an array and compute the total and the best when asked, with a loop over the array. That works, but then total() and best() take O(n) time for n turns, and the object grows with every add. Keeping two numbers instead costs a constant amount of memory and makes every method O(1). The price is that the keeper forgets the individual turns, which is fine because nobody asks for them.

    A trace

    Adding the turns 7, 7, 3 and 9 to a new keeper gives these values after each add:

    addtotal()best()
    777
    7147
    3177
    9269

    The second 7 doesn't change the best: the comparison uses >, so an equal score leaves the best as it is, which is the same number anyway. The 3 adds to the total but is smaller than the best, so the best stays. After two reset() calls in a row, both methods still return 0: resetting a fresh keeper is harmless.

    Edge cases

    • Before any add, both total() and best() return 0.
    • add(0) changes neither number, since 0 is not greater than the starting best of 0.
    • After reset(), the keeper behaves like a new one. The best only counts turns added later.

    How the object keeps its state

    Each new ScoreKeeper() creates its own object with its own totalPoints and bestPoints. Two keepers never share their numbers: adding points to one leaves the other untouched. The constructor runs once per object, which is why it is the right place to give the fields their first values.

    The methods add and reset change the object and return nothing. total and best change nothing and return a value. Splitting the work this way keeps each method small: the code that updates the numbers is in one place, and the code that reports them just reads the fields.

    Notice that reset() repeats the two assignments of the constructor. That is normal for a beginner class. Later, you can have the constructor call this.reset() to say it once, but both versions behave the same.

    Keep going with JavaScript500+ step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free