Pebellslearn by building
0%
RoadmapMatrixCatalogSandboxSign in
Fundamentals  ›  Lesson

Dictionaries

Dictionaries 16 minMapping keys to values (the menu of Python)
You're building a piece ofBill Splitter & Tip Calculator
This piece — order_total(): Maps dish → price and totals an order.
Scenario The kitchen keeps a price menu (dish → price). To bill the table, the app looks up every dish the table ordered and adds the prices.
Your task
Build order_total(order, menu). Given a list of ordered dishes and a menu dict that maps dish → price, return the total price. Unknown dishes count as 0. Example: order_total(["soup", "steak"], {"soup": 10, "steak": 20}) → 30.

Dictionaries

A dictionary (dict) maps keys to values — like a real menu maps a dish to its price. When you need to look something up by name rather than by position, a dict is the tool. This lesson builds the app's menu and totals an order from it.

Every box is live — edit and ▶ Run.


Part 1 — Key → value

A dict uses curly braces with key: value pairs. Look a value up by its key with square brackets:

python

Keys are usually strings (like here), but can be numbers too. Each key is unique — and "unique" has a consequence people don't expect:

What does this print?
menu = {"soup": 10, "steak": 20, "soup": 12}
print(menu["soup"])

❓ Cross-question — "Is a Python dict a JS object or a Map?" Closer to a Map: keys can be any hashable value (not only strings), insertion order is preserved, and you access with d["soup"], never d.soup (dot-access is for object attributes, not dict keys). For typing, dict[str, float] ≈ TS Record<string, number>.

Watch out: looking up a missing key with [] crashes. Run this to see the KeyError:

python

Part 2 — .get(): safe lookups with a default

menu.get(key, default) returns the value if the key exists, or the default if it doesn't — no crash. This is how you handle "dish not on the menu":

python

You'll reach for .get(..., 0) constantly when adding up numbers from a dict.

❓ Cross-question — "Does menu["pizza"] return undefined like a JS object?" No — it raises KeyError and stops the program. That's exactly why .get(key, default) exists — the safe lookup (≈ JS obj[key] ?? fallback). Use [] only when a missing key genuinely is a bug you want to hear about loudly.


Part 3 — Add, update, and check

Dicts are mutable — assign to a key to add or overwrite it, and use in to check for a key:

python

Part 4 — Looping over a dict

Looping a dict directly gives you its keys. Use .values() for the values and .items() for both at once:

❓ Cross-question — "What's the Object.entries / Map.entries equivalent?" It's .items()for k, v in d.items()for (const [k, v] of Object.entries(obj)). And plain for k in d gives keys (≈ Object.keys), .values() gives values. No Object.keys() wrapper needed to iterate.

python

Part 5 — Total an order from the menu

Now put it together: walk the order, look each dish up with .get(dish, 0), and add it to a running total (the accumulator pattern from Lesson 5):

python

Part 6 — Lists vs Tuples vs Sets vs Dictionaries

You've now met all four of Python's built-in collections. They look alike but each is built for a different job. Here's the whole picture on one screen:

Type Written as Ordered? Changeable? Duplicates? Access by
List ["soup", "steak"] Yes Yes Allowed position — dishes[0]
Tuple ("soup", "steak") Yes No (frozen) Allowed position — point[0]
Set {"soup", "steak"} No Yes No (unique) membership — x in s
Dict {"soup": 10} Yes Yes Keys unique key — menu["soup"]

Dicts also remember the order you inserted the keys (Python 3.7+).

Same dishes, four different tools — run this and watch how each one behaves:

python

The { } trap

Sets and dicts both use curly braces, so an empty {} is a dict, not a set — for an empty set you must write set():

python

Which one do I reach for?

  • An ordered list you'll add to or change → list
  • A fixed group that must never change (a coordinate, a row) → tuple
  • Only the unique values, or fast "is it in here?" checks → set
  • Look something up by name (dish → price, user → email) → dict

Part 7 — Idioms & real-world patterns

Dicts are the workhorse of real Python — config, JSON, counting, grouping. These are the moves you'll use daily.

Dict comprehensions & dict(zip(...))

Build a dict in one expression:

python

.setdefault and defaultdict — accumulate into a dict

Grouping items by a key is a daily task. .setdefault(key, []) gets-or-creates the bucket; collections.defaultdict does it automatically:

python

Coming from Java: by_table.setdefault(k, []).append(v) is map.computeIfAbsent(k, x -> new ArrayList<>()).add(v).

Counter — count things in one line

python

Merging dicts — defaults + overrides

python

Coming from JS: {**defaults, **user} is spread merge {...defaults, ...user} — later keys win, exactly the same.

Copying a dict — = does not do it

Exactly the trap you met with lists in lesson 11, and it bites just as often here. = gives a second name for one dict, not a second dict:

step through it
1menu = {"soup": 10}
2alias = menu
3alias["soup"] = 99
4real = menu.copy()
5real["soup"] = 1
6print(menu, real)

Step it and watch menu change on the alias[...] = 99 line, then not change when real is edited. dict(menu) and {**menu} do the same job as .copy().

All three are shallow — the outer dict is new, but nested values are still shared. If a value is itself a list, mutating it through either name shows up in both, and you want copy.deepcopy() instead.

Essentials: .update / .pop / del, and inverting

python

Reminder (hashability): dict keys must be immutable/hashable — str, int, tuple are fine; a list key raises TypeError (see Lesson 11).


🎯 Your turn

Write order_total(order, menu) — it totals the prices of the ordered dishes, treating any dish not on the menu as 0:

  • order_total(["soup", "steak"], {"soup": 10, "steak": 20})30
  • order_total(["soup", "cake"], {"soup": 10})10

Hint — start total = 0, loop the order, and add menu.get(dish, 0) each time. Return total.

Then press ▶ Run, tap the Live App try chips, and hit ✓ Check. Green = this piece of the app is built. ✅

Practice — level up

0/5 solved

Solve each in its own editor — Run to try, Check to grade. Stuck? Reveal a Hint, or the full Solution + walkthrough.

Warm-upTotal the values

Write total_values(d) returning the sum of all the dict's values (0 for an empty dict).

dict-valuessum
DrillLongest key

Write longest_key(d) returning the key with the most characters. (Assume one clear winner.)

max-key
BuildGroup by first letter

Write group_by_first(words) returning a dict mapping each first letter to the list of words starting with it, order preserved.

setdefaultloop
BossWord frequency

Write word_count(text) returning a dict of word -> count (split on whitespace).

dict.getsplitloop
CapstoneMerge menus

Write merge_menus(base, *overrides) merging any number of override dicts onto base, left to right (later values win). Don't mutate the inputs.

*argsdict-merge
order_total.py
Call the function you wrote — just like your app's frontend would. Edit the inputs and hit Call.
order_total(order, menu) → floatTotal price of an order, looking each dish up in the menu.
try:
returned
Press Run to execute your code.
Press Check to run the tests for this lesson.