Start learning free

LESSON 140 OF 679 · DICTIONARIES - CHALLENGES

Sell an Item in Rust: Counting Down in a HashMap

Practice Read the full explanation

You get inventory, a HashMap from item names to how many of each item a shop has. You also get the item a customer just bought.

Sell one item and return the updated dictionary.

The count of item drops by 1. When it reaches 0, remove item from the dictionary. The order of the entries doesn't matter.

 

Example 1:

Input: inventory = {"apples": 5, "pears": 2}, item = "apples"
Output: {"apples": 4, "pears": 2}
Explanation: One apple is sold, so 5 - 1 = 4 apples are left.

Example 2:

Input: inventory = {"cups": 1, "plates": 3}, item = "cups"
Output: {"plates": 3}
Explanation: The last cup is sold. No cups are left, so "cups" is removed.

Example 3:

Input: inventory = {"pen": 1}, item = "pen"
Output: {}
Explanation: The only pen is sold. Nothing is left in the shop.

 

Constraints:

  • item is always a key of inventory.
  • Every count is at least 1.
  • Item names are single lowercase words.
STEP 1 OF 4
    Ctrl + Enter to check

    RUST · DICTIONARIES - CHALLENGES

    Sell An Item in Rust, explained

    Problem overview

    The function sell_item receives inventory, a HashMap from item names to how many of each a shop has, and the item a customer just bought. It sells one: the count of that item drops by 1, and when it reaches 0 the item is removed from the map altogether. The order of the entries doesn't matter.

    With {"apples": 5, "pears": 2} and "apples", apples drop to 4. With {"cups": 1, "plates": 3} and "cups", the last cup is sold, so "cups" disappears and only plates remain. The constraints promise that item is always a key and that every count is at least 1.

    Approach

    Work out the count after the sale, then decide between two outcomes. If nothing is left, delete the key. Otherwise, store the smaller count under the same key. A map that keeps entries with a count of 0 would be wrong here, because the task says a sold-out item leaves the dictionary.

    Step by step

    1. Read the current count with inventory[&item] and subtract 1, storing the result in remaining. Indexing with &item borrows the key, so item stays available. Square brackets are safe here because the item is always in the map.
    2. If remaining == 0, call inventory.remove(&item). The key and its value both go.
    3. Otherwise, in an else branch, call inventory.insert(item, remaining). The key exists already, so insert replaces its value with the new count. This is the one place where the replacing behaviour is what we want.
    4. After the branches, return inventory. Only the entry of item changed, or it is gone.

    The solution in Rust

    impl Solution {
        fn sell_item(mut inventory: HashMap<String, i32>, item: String) -> HashMap<String, i32> {
            let remaining = inventory[&item] - 1;
            if remaining == 0 {
                inventory.remove(&item);
            } else {
                inventory.insert(item, remaining);
            }
            return inventory;
        }
    }
    

    Notice how item is used. The remove branch borrows it with &item, while the insert branch gives it away. That is allowed because only one branch runs. The number remaining is a plain i32, copied out of the map, so the map isn't borrowed any more when the if starts. If you tried to keep a reference into the map while calling insert, the borrow checker would refuse.

    Following two calls

    For apples, remaining is 5 - 1 = 4. That isn't 0, so the else branch stores 4 under "apples". For cups, remaining is 1 - 1 = 0, so the if branch deletes "cups", and the map is left with plates only. The pears and plates entries are never touched. Computing remaining first, before any change, keeps the two branches simple, because each one only has to do one thing with a number that is already known.

    Edge cases and common mistakes

    • The last unit. A count of 1 becomes 0, and the key must be removed. Leaving "pen": 0 in the map is a wrong answer.
    • Removing at every sale. The remove belongs only to the sold-out case.
    • A shop with one item. Selling the only pen leaves an empty map, which prints nothing.
    • Using the wrong key type. The map has String keys, so a lookup needs &item, a reference to a String.

    Testing

    The lesson's tests print the map as key: value lines sorted by key. Results from running the function:

    • {"apples": 5, "pears": 2}, "apples" prints apples: 4 and pears: 2.
    • {"cups": 1, "plates": 3}, "cups" prints only plates: 3.
    • {"pen": 1}, "pen" gives an empty map, so there is no line to print.
    Keep going with Rust500+ step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free