MediumPattern matchingNot started

Bakery order tally

A bakery's counter tablet records what is set aside for a customer. Each line is a command: add <item> <qty> adds that many of an item, remove <item> <qty> takes that many away (a count never goes below zero, and removing an item that was never added does nothing), and done ends the order. After done, print every item whose count is above zero as <item>: <count>, one per line in alphabetical order of item name, or the single word empty if nothing is left.

Input

One command per line: add <item> <qty>, remove <item> <qty> or done. Item names are single lowercase words; quantities are positive integers. The last command processed is done.

Output

One line per item with a positive count, <item>: <count>, sorted by name; or empty.

Example 1

Input

add croissant 3
add rye 2
remove croissant 1
done

Output

croissant: 2
rye: 2

3 croissants minus 1 leaves 2; rye stays at 2. Names are printed in alphabetical order.

Example 2

Input

add bun 1
remove bun 5
done

Output

empty

Removing more than there is drops the count to zero, and zero counts are not printed, so the tally is empty.

Constraints

  • 1 <= number of commands <= 1000
  • 1 <= qty <= 1000
  • Item names contain only lowercase letters

Hints

Hint 1 of 3

Split each line into words with split_whitespace().collect::<Vec<&str>>() and match on parts.as_slice() with slice patterns such as ["add", item, qty].

Hint 2 of 3

A BTreeMap<String, u32> keeps its keys sorted, so printing in alphabetical order comes for free.

Hint 3 of 3

For remove, saturating_sub stops the count at zero instead of underflowing; entry(...).or_insert(0) is the easy way to add.

Solution

Show a reference solution and explanation
 Rust · reference solution
use std::collections::BTreeMap;
use std::io::{self, BufRead};

fn main() {
    let stdin = io::stdin();
    let mut tally: BTreeMap<String, u32> = BTreeMap::new();
    for line in stdin.lock().lines() {
        let line = line.unwrap();
        let parts: Vec<&str> = line.split_whitespace().collect();
        match parts.as_slice() {
            ["done"] => break,
            ["add", item, qty] => {
                let qty: u32 = qty.parse().unwrap();
                *tally.entry(item.to_string()).or_insert(0) += qty;
            }
            ["remove", item, qty] => {
                let qty: u32 = qty.parse().unwrap();
                if let Some(count) = tally.get_mut(*item) {
                    *count = count.saturating_sub(qty);
                }
            }
            _ => {}
        }
    }
    let mut printed = false;
    for (item, count) in &tally {
        if *count > 0 {
            println!("{}: {}", item, count);
            printed = true;
        }
    }
    if !printed {
        println!("empty");
    }
}

Why it works

Slice patterns let one match do both the dispatch on the command word and the extraction of its arguments: ["add", item, qty] only matches a three-word line whose first word is add, and binds the other two. The wildcard arm makes the match exhaustive. A BTreeMap is chosen over a HashMap for one reason: its iteration order is the sorted key order, which is exactly the output order required, whereas a HashMap would print in an unspecified order. saturating_sub expresses the never-below-zero rule directly on u32 without a separate check, and break on done stops reading even if more lines follow.

Your program
use std::collections::BTreeMap;
use std::io::{self, BufRead};

fn main() {
    let stdin = io::stdin();
    let mut tally: BTreeMap<String, u32> = BTreeMap::new();
    for line in stdin.lock().lines() {
        let line = line.unwrap();
        let parts: Vec<&str> = line.split_whitespace().collect();
        match parts.as_slice() {
            ["done"] => break,
            // add arms for ["add", item, qty] and ["remove", item, qty]
            _ => {}
        }
    }
    // print every item with a count above zero as "<item>: <count>",
    // in alphabetical order, or "empty" if there are none
}
Run is not available for Rust in the browser yet. Write your program here, then download it and run it locally with rustc 1.94 against the examples above. The reference solution below was verified the same way.

Tests: 5 cases including the examples. Passing every test marks the exercise solved in this browser.

How this page was checked. Every program on it was run with rustc 1.94 at build time by the publishing checks, and the output shown is what it printed. Running Rust inside the browser is not available yet, so the Run button is absent rather than pretending; copy the code and run it with rustc 1.94 locally.