Pebellslearn by building
0%
RoadmapsMatrixCoursesSandboxSign in
Data Structures, Lightly  ›  Lesson

HashMaps

HashMaps 13 minHow dict turns a key straight into a bucket, and why only hashable things can be keys
Your task
Write count_frequencies(items). Return a dict mapping each distinct item to how many times it appears. Example: count_frequencies(["a", "b", "a"]) -> {"a": 2, "b": 1}.

HashMaps

In short: a Python dict is a hash table — every key is run through a hash function that picks which "bucket" it lives in, so d[key] jumps almost straight there instead of scanning every entry. That's why dict lookup stays fast even as a dict grows huge, unlike checking x in a_list, which has to walk the list one item at a time. The catch: a key's hash must never change while it's stored, which is why only hashable things — strings, numbers, tuples of hashable things — can be keys, and a list can't. The DSA course covers collisions and hash-function design in depth; this is the Python-shaped version: how to actually use the hash table you never had to build.

A funnel labelled hash("b") dropping the key "b" into bucket 3 of five numbered buckets (0-4); a second key "z" drops into bucket 1 through the same funnel; a note: "same key, same hash, same bucket — every time"

Check if a username is already taken

A signup form checks a new username against everyone already registered. With 50 users, scanning a list to check membership is invisible. With 5 million, it isn't — and that's the mistake: a list in check and a dict in check look identical in code, but they don't cost the same thing:

python

hash("asha_k") is the same number every time in one run — that's what lets a dict go straight to the right bucket instead of comparing the string against every username already stored:

What does this print?
taken = {str(i): True for i in range(10000)}
print("9999" in taken)

A list of 10,000 usernames checked with in walks, on average, half of them before finding (or missing) a match. A dict with the same 10,000 keys jumps to the right bucket in roughly the same handful of steps no matter how many keys it holds. That's the entire reason to reach for {username: True} or a set instead of a list the moment "is this already here?" becomes a real question.

Store a shopping cart by item combo

A grocery app wants to remember a quantity for each combination of items a customer buys together — say, ["milk", "bread"] as one cart line. The natural instinct is to use that list straight as the dict key:

broken — fix itTypeError

Run it and read the error. Then make it work using an immutable key.

The mistake is reasonable — a list and a tuple hold the same data — but a key's hash is computed once and must stay valid for as long as it's stored in the dict. A list can be appended to or sorted after it's used as a key, which would silently change its hash and corrupt the bucket it's sitting in. Python refuses to take that risk and raises TypeError up front. A tuple can't be mutated after creation, so its hash is safe forever — swap ["milk", "bread"] for ("milk", "bread") and the same idea works. Strings, numbers and tuples (as long as everything inside them is also hashable) all work fine as keys. Lists, dicts and sets never do.

Count votes in a poll

The single most common real use of a hash map: counting.

python

dict.get(v, 0) reads the current count, or 0 the first time — one line, no if v in counts check needed first. The trap people hit instead is reading a key that was never counted with plain brackets:

broken — fix itKeyError

Run it, read the error. Then print 0 for a menu item that isn't in totals.

d[key] demands the key exist and crashes if it doesn't; d.get(key, default) always returns something. That one-word swap (get instead of []) is the pattern (and setdefault, for grouping into lists) that covers most of what you'll reach for a dict to do.

Quick practice

What does this print?
seen = {}
for ch in "banana":
    seen[ch] = seen.get(ch, 0) + 1
print(seen["a"], seen["n"], seen.get("z", 0))
What does this print?
d = {}
d[(1, 2)] = "point A"
print(d[(1, 2)])

🎯 Your turn

Write count_frequencies(items). Return a dict mapping each distinct item to how many times it appears in items:

  • count_frequencies(["a", "b", "a"]) → {"a": 2, "b": 1}
  • count_frequencies([]) → {}

Hint — counts[item] = counts.get(item, 0) + 1, once per item, starting from an empty dict.

Press ▶ Run, try the Live App chips, then ✓ Check. After that, the practice set below groups, dedupes and inverts using the same hashing underneath.

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-upAny duplicates at all?

Write has_duplicates(items) returning True if any value appears more than once, using a set (a hash table with no values, just keys).

hash-tablesdedupe
DrillThe first value seen twice

Write first_repeated(items) returning the first value that has already been seen, using a set to remember what's been seen so far. Return None if nothing repeats.

hash-tables
BuildSwap keys and values

Write invert_dict(d) returning a new dict where every (string) value in d becomes a key, mapped back to its original key.

dictionarieshash-tables
BossGroup words by their length

Write group_by_length(words) returning a dict mapping each length (as a STRING key, e.g. "2") to the list of words of that length, in their original order. Use dict.setdefault.

dictionarieshash-tablesgrouping
CapstoneThe most frequent item

Write most_common(items) returning the item that appears most often. On a tie, return whichever of the tied items appears FIRST in items.

hash-tablescounter
count_frequencies.py
Call the function you wrote — just like your app's frontend would. Edit the inputs and hit Call.
count_frequencies(items) → dictCount how many times each distinct item appears.
try:
returned
No app map for this section yet.
Press Run to execute your code.
Press Check to run the tests for this lesson.