Concepts / Maps and key-value pairs

Maps and key-value pairs

Each stored pair is one entry: a name such as Ana paired with the number that belongs to Ana.

  • Programming

Predicting a Lookup for a Missing Name

Imagine a phone book where each name is paired with a telephone number. You give the book a name and it hands back the number stored with that name. A map works the same way: it pairs keys with values, and you ask for a value by giving it a key. The source material uses this phone book to make the idea concrete, with a name going in and a telephone number coming out.

What do you think happens?

The map already stores entries for Ana and Ben. What does the lookup return when you ask for the name Cara, which was never stored?

  • An error saying the name is missing

  • 0

  • Ana's number, because it is the first entry

Reveal answer
Answer:

0. The map also gains a new entry for Cara that holds 0.

When a key is not found, the map does not just report absence. It creates an entry for that key, gives it a default value, and returns that default. For an integer value type the default is 0. Because the lookup inserts a new entry, the map is changed by the lookup itself.

Keep this surprise in mind. The rest of the article traces exactly what happens inside one subscript operation so you can predict this result instead of being caught off guard by it.

Keys and Values in a Map

Definition

A map pairs keys with values. You supply a key of one type, and the map returns the value of a second type that is associated with that key. The first type is called the key type, and the second is called the value type, or the mapped type.

In the phone book example, the key type is a string (a name) and the value type is an integer (a telephone number). Each stored pair is one entry: a name such as Ana paired with the number that belongs to Ana. The map is the whole collection of these entries.

Writing a key in square brackets after the map is called subscripting. This is the moment the map is asked for a value, and it is the single line of code this article examines closely.

How a Subscript Finds an Entry

The source describes a small function that takes a string, uses that string as a key into a map called phone_book, and returns whatever value the map gives back. The function is named get_number. Its entire body is one return statement built around one subscript.

Output (expected)
1002
Notes on the code
  • name is the key that is passed in
  • phone_book[name] is the subscript that searches the map
  • the value stored under that key is what comes back
Ana1001Bensubscript key1002returned valueBen1002matches keyreturns value
Given the stored entries, the subscript with the key Ben selects the entry whose key matches Ben and returns the value stored with it.

Notice what the function does not do. It does not loop over the entries, compare names by hand, or search for anything itself. The subscript operation performs the entire search. The function is a named wrapper around that one operation, which is why the source calls subscripting essentially the lookup that get_number performs.

Key Present Versus Key Absent

A subscript has two possible paths, and which one runs depends entirely on whether the key is already in the map. When the map is indexed by a value of its key type, the map is searched for that key. The two outcomes are different enough that you should trace each one separately.

Subscript with keyKey in map?Return stored valuemap unchangedAdd entry withdefault 0Return default 0yesno
The subscript first asks whether the key is present. A present key returns the stored value with no change to the map. An absent key adds an entry with a default value and returns that default.

On the present path, nothing is added to the map. The stored value simply comes back. On the absent path, the map grows by one entry before the default value is returned. The result you see on the screen is therefore not the whole story: the absent path also leaves a permanent change behind.

What Changes When a Default Is Inserted

When a key is not found, the map creates an entry for that key and gives the entry a default value. This is called default value initialization. The default depends on the type of the value. The source gives the integer case: the default for an integer type is 0.

Ana: 1001Ana: 1001Ben: 1002Ben: 1002Cara: 0
Before the lookup the map holds two entries. After looking up Cara, which was absent, the map holds a third entry for Cara with the default value 0.
Worked example

Looking up a missing name twice

The map starts with Ana mapped to 1001 and Ben mapped to 1002. Trace two calls: get_number("Cara") followed by get_number("Cara").

  1. 1
    Check the starting map

    The map holds two entries, Ana and Ben. The key Cara is not among them, so the key is absent.

  2. 2
    First call, key absent

    The subscript cannot find Cara, so the map creates an entry for Cara and gives it the default value 0. The map now holds three entries.

  3. 3
    First call returns

    The default value 0 is returned to the caller. The caller sees 0, and the map has permanently changed.

  4. 4
    Second call, key present

    Cara is now stored in the map, so the subscript finds the present key. It returns the stored value 0 and adds nothing new.

  5. 5
    Compare the two calls

    Both calls returned 0, but only the first one changed the map. The second call was a plain read of an existing entry.

Result

Both calls return 0. The map grows from two entries to three during the first call and stays at three during the second.

Tracing Where a Lookup Surprises You

When a returned value does not match what you expected, two questions locate the cause. First, was the key present when the subscript ran? Second, what was stored under that key at that moment? Checking those two facts usually explains the returned value. Write them down as you trace, because the answer to the first question can change the map for the second.

Output (expected)
step 1 returns 0; step 2 returns 1001
Notes on the code
  • The first call looks like a plain lookup but also inserts Ben
  • The second call is unaffected by the first except through the map's contents
  • Tracing the key and the stored contents at each step reveals the result

Mistakes Learners Make With Missing Keys

  • Common mistake

    Assuming a missing-key lookup leaves the map unchanged

    What it looks like

    After get_number("Cara") returns 0, the learner assumes Cara is still not in the phone book.

    Why it fails

    The absent path inserts an entry with the default value, so the map grows by one entry.

    Do this instead

    Treat every subscript on a possibly absent key as a read that may also write.

  • Common mistake

    Reading the returned value as the stored data

    What it looks like

    The learner sees 0 for Cara and concludes Cara's real number is 0.

    Why it fails

    The 0 may be the default value inserted by initialization, not a value anyone stored.

    Do this instead

    Trace whether the key was present before the subscript ran.

  • Common mistake

    Looking at the map's contents only after the lookup

    What it looks like

    The learner checks the map after a call and cannot tell whether the entry was there before.

    Why it fails

    The state before the call is needed to explain the result.

    Do this instead

    Record the entries before the call, then the key used, then the entries after.

  • Common mistake

    Confusing the key type with the value type when reading a lookup

Choosing a Default Value Deliberately

Best practice

Pick the value type and the meaning of its default together. The source's choice of 0 for an invalid telephone number shows the intent behind a default: it is a meaningful placeholder for the program's design, not a universal constant. A default that nobody chose on purpose can quietly look like real data.

Tracing Lookups by Hand

MEDIUM

The map starts with Ana mapped to 1001 and Ben mapped to 1002. Trace get_number("Dan") and then get_number("Ben"). For each call, state whether the key was present, what the subscript returned, and what the map holds afterward.

Hints
  • Before each call, ask whether the key is in the map at that exact moment.
  • A missing key both returns a value and adds an entry to the map.
  • Write the map's entries after each call so the second call sees the correct contents.

Key Takeaways

  • A map pairs keys of one type with values of a second type, and a subscript with a key returns the value associated with that key.
  • A subscript on a present key returns the stored value and leaves the map unchanged.
  • A subscript on an absent key inserts an entry with a default value, which is 0 for an integer value type, and returns that default.
  • Because a missing-key lookup changes the map, trace whether the key was present and what was stored under it to explain any surprising result.

Related concepts

More concepts starting with M