Start learning free

LESSON 250 OF 569 · FUNCTIONS & CLASSES

Water Bottle Class in JavaScript

Practice O(1) Read the full explanation

A water bottle holds at most a fixed amount of water: you can't pour in more than fits, and you can't drink more than is in it. Write a class WaterBottle that keeps track of how much water is in one bottle as you fill it and drink from it. Amounts are whole milliliters.

Implement the class WaterBottle:

  • WaterBottle(capacity) creates an empty bottle that holds at most capacity.
  • fill(amount) pours in amount, but only as much as fits: the water never goes above the capacity. It returns how much actually went in.
  • drink(amount) drinks amount, but only as much as is in the bottle: the water never goes below 0. It returns how much was actually drunk.
  • level() returns how much water is in the bottle now.

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

 

Example 1:

Input:
    WaterBottle(500)
    fill(200)
    drink(50)
    level()
Output: 200 50 150
Explanation: The empty bottle has room for 500, so all 200 go in. Drinking 50 of them leaves 150.

Example 2:

Input:
    WaterBottle(300)
    fill(250)
    fill(100)
    level()
    fill(10)
Output: 250 50 300 0
Explanation: After the first fill, only 50 more fit: fill(100) pours in 50 and returns 50, and the bottle
is full. A full bottle takes nothing more, so fill(10) returns 0.

Example 3:

Input:
    WaterBottle(100)
    drink(10)
    fill(60)
    drink(100)
    level()
    drink(5)
Output: 0 60 60 0 0
Explanation: The bottle starts empty, so the first drink gets nothing. drink(100) can only drink
the 60 that are there, which empties the bottle again.

 

Constraints:

  • 1 <= capacity <= 104
  • 0 <= amount <= 104
  • At most 250 operations on one WaterBottle.

Note:

Each method should run in O(1) time: the bottle only needs to remember its capacity and how much water it holds.

STEP 1 OF 9
    Ctrl + Enter to check

    JAVASCRIPT · FUNCTIONS & CLASSES

    Water Bottle in JavaScript, explained

    Problem overview

    A bottle holds at most a fixed amount of water. WaterBottle tracks one bottle as you fill it and drink from it, in whole milliliters.

    • new WaterBottle(capacity) creates an empty bottle.
    • fill(amount) pours in as much as fits and returns how much actually went in.
    • drink(amount) drinks as much as the bottle holds and returns how much was actually drunk.
    • level() returns the water in the bottle now.

    With capacity 300, fill(250) returns 250. Then fill(100) returns only 50, because only 50 more fit, and the bottle is full. A full bottle takes nothing, so fill(10) returns 0.

    Approach

    The object needs to remember two numbers: the capacity, which never changes, and the water, which starts at 0. Both operations have the same shape. Work out how much can really be moved: the smaller of the requested amount and the limit. Fill's limit is the room left, capacity - water. Drink's limit is the water in the bottle. Then change the water by that amount and return it. Because the moved amount is never more than the limit, the water stays between 0 and the capacity. Each method takes O(1) time.

    Step by step

    1. Store the capacity in this.capacity.
    2. Start empty: set this.water to 0. The field is not called level, because that would hide the method level().
    3. Fill. Declare added as Math.min(amount, this.capacity - this.water).
    4. Update the water with this.water += added. Adding added rather than amount is what keeps the bottle from overflowing.
    5. Return added, so the caller learns whether everything fit.
    6. Drink works the same way: drunk is the smaller of amount and this.water. Subtract it with -=, then return it.
    7. Level returns this.water.

    The solution in JavaScript

    class WaterBottle {
        constructor(capacity) {
            this.capacity = capacity;
            this.water = 0;
        }
    
        fill(amount) {
            let added = Math.min(amount, this.capacity - this.water);
            this.water += added;
            return added;
        }
    
        drink(amount) {
            let drunk = Math.min(amount, this.water);
            this.water -= drunk;
            return drunk;
        }
    
        level() {
            return this.water;
        }
    }
    

    Using the class

    let b = new WaterBottle(500);
    console.log(b.fill(200), b.drink(50), b.level());
    
    b = new WaterBottle(300);
    console.log(b.fill(250), b.fill(100), b.level(), b.fill(10));
    
    b = new WaterBottle(100);
    console.log(b.drink(10), b.fill(60), b.drink(100), b.level(), b.drink(5));
    

    This prints:

    200 50 150
    250 50 300 0
    0 60 60 0 0
    

    In the last line the first drink gets nothing from the empty bottle. After fill(60), drink(100) can only take the 60 that are there, which empties the bottle again.

    Common mistakes

    • Adding amount instead of added. The water would go past the capacity.
    • Returning amount. The methods return what really happened, so fill(100) on a bottle with 50 of room returns 50.
    • Mixing up the limits. Fill is limited by the room left (capacity - water), not by the capacity; drink is limited by the water, not by the capacity.
    • A field named level. It would hide the level() method, and bottle.level() would fail with a TypeError.

    A trace

    This is a bottle with capacity 100, one call after another:

    CallReturnsLevel after
    fill(70)7070
    fill(70)30100
    drink(30)3070
    drink(200)700

    The second fill(70) asks for more than fits: the room left is 100 - 70 = 30, so Math.min(70, 30) is 30 and the bottle ends exactly full. The last drink(200) asks for more than the bottle holds, so it takes the 70 that are there.

    Edge cases

    • Zero amounts. fill(0) and drink(0) move nothing and return 0. The formula needs no special case: the smaller of 0 and anything non-negative is 0.
    • Exactly the room left. On an empty bottle with capacity 100, fill(100) returns 100 and the level is 100. A further fill(1) returns 0.
    • Draining the bottle. From that full bottle, drink(100) returns 100 and level() returns 0.
    • Several bottles. Each new WaterBottle(...) has its own capacity and water, so filling one never changes another.

    Why the return values matter

    fill and drink both change the bottle and report what happened. A plain "void" method would hide the difference between a fill that fitted completely and one that was cut short. Returning the moved amount lets the caller compare it with the request: if fill(100) returns 50, half of the water did not fit.

    An equivalent version

    The same rules can be written with an if instead of Math.min:

    fill(amount) {
        let room = this.capacity - this.water;
        if (amount > room) {
            amount = room;
        }
        this.water += amount;
        return amount;
    }
    

    It does the same thing, but Math.min says it in one line: take the smaller of the two numbers.

    Run on a bottle with capacity 300, this version gives the same answers as the solution: fill(250), fill(100) and fill(10) return 250, 50 and 0, and the water ends at 300.

    The rule the class protects

    At all times, the water is between 0 and the capacity. Nothing in the class checks this after the fact. Instead, each method makes sure the rule holds before it changes anything: fill never adds more than the room left, so the water can reach the capacity but not pass it; drink never removes more than the water, so it can reach 0 but not go below. Designing a method so that the rule cannot be broken is simpler than repairing the value afterwards, and the same idea comes back in every class that has limits to respect.

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