Exercise: Classes & Objects
This exercise puts the classes and objects lesson into code.
Scenario
Model a shopping cart that manages a collection of items with prices and quantities. The cart starts empty and is driven by a list of commands. Each command produces exactly one output line. The cart is responsible for all decisions about its own state — adding items, removing them, computing totals, and counting quantities. No external code reaches into the cart's internals.
Commands
The program reads one command per line and prints one line of output per command.
| Command | Behavior | Output |
|---|---|---|
["add", item, price, qty] | Add qty of item at price per unit. If item is already in the cart, increase its quantity by qty. | "Added <qty> <item>" |
["remove", item] | Remove item entirely from the cart. | "Removed <item>", or "<item> not in cart" if the item does not exist |
["total"] | Report the cumulative price * qty across all items. | "Total: <sum>" |
["count"] | Report the total quantity across all items. | "Items: <sum>" |
Prices and quantities are integers.
7 add apple 2 3 add banana 1 5 total count add apple 3 2 remove banana total
Added 3 apple Added 5 banana Total: 11 Items: 8 Added 2 apple Removed banana Total: 10
Your task
Implement the skeleton in the editor below so the commands produce the output shown in the example above.
How to approach it
The solution centers on a single ShoppingCart class that owns all item data and exposes it only through four methods.
Define a single ShoppingCart class that owns a private map from item name to price and
quantity. The map is the only place item data lives — callers never touch it directly.
Each of the four operations is a method on the class. add checks whether the item already
exists: if it does, it updates the quantity; if it does not, it inserts a new entry. remove
checks for existence before deleting and returns the appropriate message either way. total
and count both iterate the map, summing across all entries. This is the point of the
exercise: the logic that reads and modifies the cart's data lives with the data itself, not in
the command loop.
The command loop in the skeleton already dispatches each command to the matching method and collects the return value. Your job is to implement the four method bodies so the output matches the specification above.
Solution
The reference design places a single ShoppingCart class at the center. The class stores items
in a private map from name to price-and-quantity pair, so the only way item data moves is through
the four methods. add checks whether the item already exists and either inserts a fresh entry
or increments the existing quantity. remove guards against unknown items before deleting.
total and count both iterate the map in a single pass, accumulating their respective sums
and returning the formatted line — keeping all cart logic inside the class that owns the data.
1def run_cart(instructions: list[list[str]]) -> list[str]:
2 class ShoppingCart:
3 def __init__(self) -> None:
4 self._items: dict[str, list] = {} # item -> [price, qty]
5
6 def add(self, item: str, price: int, qty: int) -> str:
7 if item in self._items:
8 self._items[item][1] += qty
9 else:
10 self._items[item] = [price, qty]
11 return f"Added {qty} {item}"
12
13 def remove(self, item: str) -> str:
14 if item not in self._items:
15 return f"{item} not in cart"
16 del self._items[item]
17 return f"Removed {item}"
18
19 def total(self) -> str:
20 s = sum(v[0] * v[1] for v in self._items.values())
21 return f"Total: {s}"
22
23 def count(self) -> str:
24 c = sum(v[1] for v in self._items.values())
25 return f"Items: {c}"
26
27 cart = ShoppingCart()
28 output: list[str] = []
29 for instruction in instructions:
30 command = instruction[0]
31 if command == "add":
32 output.append(cart.add(instruction[1], int(instruction[2]), int(instruction[3])))
33 elif command == "remove":
34 output.append(cart.remove(instruction[1]))
35 elif command == "total":
36 output.append(cart.total())
37 elif command == "count":
38 output.append(cart.count())
39 return output
40
41if __name__ == "__main__":
42 instructions = [input().split() for _ in range(int(input()))]
43 res = run_cart(instructions)
44 for line in res:
45 print(line)
461import java.util.ArrayList;
2import java.util.Arrays;
3import java.util.HashMap;
4import java.util.List;
5import java.util.Map;
6import java.util.Scanner;
7
8class Solution {
9 static class ShoppingCart {
10 private Map<String, int[]> items = new HashMap<>(); // item -> [price, qty]
11
12 String add(String item, int price, int qty) {
13 if (items.containsKey(item)) {
14 items.get(item)[1] += qty;
15 } else {
16 items.put(item, new int[]{price, qty});
17 }
18 return "Added " + qty + " " + item;
19 }
20
21 String remove(String item) {
22 if (!items.containsKey(item)) return item + " not in cart";
23 items.remove(item);
24 return "Removed " + item;
25 }
26
27 String total() {
28 int sum = 0;
29 for (int[] v : items.values()) sum += v[0] * v[1];
30 return "Total: " + sum;
31 }
32
33 String count() {
34 int c = 0;
35 for (int[] v : items.values()) c += v[1];
36 return "Items: " + c;
37 }
38 }
39
40 public static List<String> runCart(List<List<String>> instructions) {
41 ShoppingCart cart = new ShoppingCart();
42 List<String> output = new ArrayList<>();
43 for (List<String> instruction : instructions) {
44 String command = instruction.get(0);
45 if (command.equals("add")) {
46 output.add(cart.add(instruction.get(1), Integer.parseInt(instruction.get(2)), Integer.parseInt(instruction.get(3))));
47 } else if (command.equals("remove")) {
48 output.add(cart.remove(instruction.get(1)));
49 } else if (command.equals("total")) {
50 output.add(cart.total());
51 } else if (command.equals("count")) {
52 output.add(cart.count());
53 }
54 }
55 return output;
56 }
57
58 public static List<String> splitWords(String s) {
59 return s.isEmpty() ? List.of() : Arrays.asList(s.split(" "));
60 }
61
62 public static void main(String[] args) {
63 java.util.Scanner scanner = new java.util.Scanner(System.in);
64 int instructionsLength = Integer.parseInt(scanner.nextLine());
65 List<List<String>> instructions = new ArrayList<>();
66 for (int i = 0; i < instructionsLength; i++) {
67 instructions.add(splitWords(scanner.nextLine()));
68 }
69 scanner.close();
70 List<String> res = runCart(instructions);
71 for (String line : res) {
72 System.out.println(line);
73 }
74 }
75}
761"use strict";
2
3class ShoppingCart {
4 constructor() {
5 this._items = new Map(); // item -> { price, qty }
6 }
7
8 add(item, price, qty) {
9 if (this._items.has(item)) {
10 this._items.get(item).qty += qty;
11 } else {
12 this._items.set(item, { price, qty });
13 }
14 return `Added ${qty} ${item}`;
15 }
16
17 remove(item) {
18 if (!this._items.has(item)) return `${item} not in cart`;
19 this._items.delete(item);
20 return `Removed ${item}`;
21 }
22
23 total() {
24 let sum = 0;
25 for (const { price, qty } of this._items.values()) sum += price * qty;
26 return `Total: ${sum}`;
27 }
28
29 count() {
30 let c = 0;
31 for (const { qty } of this._items.values()) c += qty;
32 return `Items: ${c}`;
33 }
34}
35
36function runCart(instructions) {
37 const cart = new ShoppingCart();
38 const output = [];
39 for (const instruction of instructions) {
40 const [command, ...args] = instruction;
41 if (command === 'add') {
42 output.push(cart.add(args[0], parseInt(args[1]), parseInt(args[2])));
43 } else if (command === 'remove') {
44 output.push(cart.remove(args[0]));
45 } else if (command === 'total') {
46 output.push(cart.total());
47 } else if (command === 'count') {
48 output.push(cart.count());
49 }
50 }
51 return output;
52}
53
54function splitWords(s) {
55 return s === "" ? [] : s.split(" ");
56}
57
58function* main() {
59 const instructionsLength = parseInt(yield);
60 const instructions = [];
61 for (let i = 0; i < instructionsLength; i++) {
62 instructions.push(splitWords(yield));
63 }
64 const res = runCart(instructions);
65 for (const line of res) {
66 console.log(line);
67 }
68}
69
70class EOFError extends Error {}
71{
72 const gen = main();
73 const next = (line) => gen.next(line).done && process.exit();
74 let buf = "";
75 next();
76 process.stdin.setEncoding("utf8");
77 process.stdin.on("data", (data) => {
78 const lines = (buf + data).split("\n");
79 buf = lines.pop();
80 lines.forEach(next);
81 });
82 process.stdin.on("end", () => {
83 buf && next(buf);
84 gen.throw(new EOFError());
85 });
86}
871class ShoppingCart {
2 private _items: Map<string, { price: number; qty: number }> = new Map();
3
4 add(item: string, price: number, qty: number): string {
5 const existing = this._items.get(item);
6 if (existing) {
7 existing.qty += qty;
8 } else {
9 this._items.set(item, { price, qty });
10 }
11 return `Added ${qty} ${item}`;
12 }
13
14 remove(item: string): string {
15 if (!this._items.has(item)) return `${item} not in cart`;
16 this._items.delete(item);
17 return `Removed ${item}`;
18 }
19
20 total(): string {
21 let sum = 0;
22 for (const { price, qty } of this._items.values()) sum += price * qty;
23 return `Total: ${sum}`;
24 }
25
26 count(): string {
27 let c = 0;
28 for (const { qty } of this._items.values()) c += qty;
29 return `Items: ${c}`;
30 }
31}
32
33function runCart(instructions: string[][]): string[] {
34 const cart = new ShoppingCart();
35 const output: string[] = [];
36 for (const instruction of instructions) {
37 const [command, ...args] = instruction;
38 if (command === 'add') {
39 output.push(cart.add(args[0], parseInt(args[1]), parseInt(args[2])));
40 } else if (command === 'remove') {
41 output.push(cart.remove(args[0]));
42 } else if (command === 'total') {
43 output.push(cart.total());
44 } else if (command === 'count') {
45 output.push(cart.count());
46 }
47 }
48 return output;
49}
50
51function splitWords(s: string): string[] {
52 return s === "" ? [] : s.split(" ");
53}
54
55function* main() {
56 const instructionsLength = parseInt(yield);
57 const instructions = [];
58 for (let i = 0; i < instructionsLength; i++) {
59 instructions.push(splitWords(yield));
60 }
61 const res = runCart(instructions);
62 for (const line of res) {
63 console.log(line);
64 }
65}
66
67class EOFError extends Error {}
68{
69 const gen = main();
70 const next = (line?: string) => gen.next(line ?? "").done && process.exit();
71 let buf = "";
72 next();
73 process.stdin.setEncoding("utf8");
74 process.stdin.on("data", (data: string) => {
75 const lines = (buf + data).split("\n");
76 buf = lines.pop() ?? "";
77 lines.forEach(next);
78 });
79 process.stdin.on("end", () => {
80 buf && next(buf);
81 gen.throw(new EOFError());
82 });
83}
841#include <algorithm>
2#include <iostream>
3#include <iterator>
4#include <limits>
5#include <sstream>
6#include <string>
7#include <vector>
8
9#include <string>
10#include <vector>
11#include <unordered_map>
12
13class ShoppingCart {
14private:
15 std::unordered_map<std::string, std::pair<int,int>> items; // item -> {price, qty}
16
17public:
18 std::string add(const std::string& item, int price, int qty) {
19 auto it = items.find(item);
20 if (it != items.end()) {
21 it->second.second += qty;
22 } else {
23 items[item] = {price, qty};
24 }
25 return "Added " + std::to_string(qty) + " " + item;
26 }
27
28 std::string remove(const std::string& item) {
29 auto it = items.find(item);
30 if (it == items.end()) return item + " not in cart";
31 items.erase(it);
32 return "Removed " + item;
33 }
34
35 std::string total() {
36 int sum = 0;
37 for (const auto& kv : items) sum += kv.second.first * kv.second.second;
38 return "Total: " + std::to_string(sum);
39 }
40
41 std::string count() {
42 int c = 0;
43 for (const auto& kv : items) c += kv.second.second;
44 return "Items: " + std::to_string(c);
45 }
46};
47
48std::vector<std::string> run_cart(std::vector<std::vector<std::string>> instructions) {
49 ShoppingCart cart;
50 std::vector<std::string> output;
51 for (const auto& instruction : instructions) {
52 const std::string& command = instruction[0];
53 if (command == "add") {
54 output.push_back(cart.add(instruction[1], std::stoi(instruction[2]), std::stoi(instruction[3])));
55 } else if (command == "remove") {
56 output.push_back(cart.remove(instruction[1]));
57 } else if (command == "total") {
58 output.push_back(cart.total());
59 } else if (command == "count") {
60 output.push_back(cart.count());
61 }
62 }
63 return output;
64}
65
66template<typename T>
67std::vector<T> get_words() {
68 std::string line;
69 std::getline(std::cin, line);
70 std::istringstream ss{line};
71 ss >> std::boolalpha;
72 std::vector<T> v;
73 std::copy(std::istream_iterator<T>{ss}, std::istream_iterator<T>{}, std::back_inserter(v));
74 return v;
75}
76
77void ignore_line() {
78 std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
79}
80
81int main() {
82 int instructions_length;
83 std::cin >> instructions_length;
84 ignore_line();
85 std::vector<std::vector<std::string>> instructions;
86 for (int i = 0; i < instructions_length; i++) {
87 instructions.emplace_back(get_words<std::string>());
88 }
89 std::vector<std::string> res = run_cart(instructions);
90 for (const std::string& line : res) {
91 std::cout << line << '\n';
92 }
93}
941class ShoppingCart
2 def initialize
3 @items = {} # item -> { price: int, qty: int }
4 end
5
6 def add(item, price, qty)
7 if @items.key?(item)
8 @items[item][:qty] += qty
9 else
10 @items[item] = { price: price, qty: qty }
11 end
12 "Added #{qty} #{item}"
13 end
14
15 def remove(item)
16 return "#{item} not in cart" unless @items.key?(item)
17 @items.delete(item)
18 "Removed #{item}"
19 end
20
21 def total
22 sum = @items.values.sum { |v| v[:price] * v[:qty] }
23 "Total: #{sum}"
24 end
25
26 def count
27 c = @items.values.sum { |v| v[:qty] }
28 "Items: #{c}"
29 end
30end
31
32def run_cart(instructions)
33 cart = ShoppingCart.new
34 output = []
35 instructions.each do |instruction|
36 command = instruction[0]
37 case command
38 when 'add'
39 output << cart.add(instruction[1], instruction[2].to_i, instruction[3].to_i)
40 when 'remove'
41 output << cart.remove(instruction[1])
42 when 'total'
43 output << cart.total
44 when 'count'
45 output << cart.count
46 end
47 end
48 output
49end
50
51if __FILE__ == $0
52 instructions = Array.new(Integer(gets, 10)) { gets.split }
53 res = run_cart(instructions)
54 res.each { |line| puts(line) }
55end
56