Start learning free

LESSON 130 OF 678 · DICTIONARIES

Reading a Swift Dictionary Key That May Be Missing

Lesson Read the full explanation

TL ; DR:

dict[key, default: 0] gives the key's value, or 0 when the key is missing.


let points: [String: Int] = ["Ana": 7]

print(points["Ana", default: 0])   // 7
print(points["Ben", default: 0])   // 0


Full lesson:

A missing key often means "none yet". A player with no points yet has 0 points.

points["Ben"] gives nil here. Its type is Int?, a number or nil. And ! on nil crashes the program.

With default:, a missing key gives the default instead. Either way, you get a plain Int.

Reading this way doesn't add the key. points still has one key afterwards.

The ?? from Optionals & Unwrapping works too, as in points["Ben"] ?? 0.


Your task

stock counts the fruit in a shop. The code reads and prints the pears.

There are no plums yet. Read "plums" with default: 0. Store it in plums.

Then print the plums line, the same way as the pears line.

Expected output:

We have 2 pears
We have 0 plums
STEP 1 OF 2
    Ctrl + Enter to check

    SWIFT · DICTIONARIES

    Dictionary Default Value in Swift, explained

    The problem with a plain lookup

    A missing key often means "none yet". A shop with no plums has 0 plums. But a plain lookup on a missing key does not give 0. It gives nil, and the type is Int?, a number that might be absent. Forcing it open with ! crashes the program when the key is missing.

    let stock: [String: Int] = ["apples": 5]
    print(type(of: stock["plums"]))
    
    Optional<Int>
    

    The default: subscript

    A dictionary has a second subscript that takes a fallback: stock["plums", default: 0]. It gives the key's value when the key exists, and the default when it doesn't. Either way, the result is a plain Int, not an optional.

    print(stock["apples", default: 0])
    print(stock["plums", default: 0])
    
    5
    0
    

    Reading this way does not add the key. After the lines above, stock.count is still 1.

    The ?? alternative

    The nil-coalescing operator gives the same answer for a read. stock["plums"] ?? 0 means "the value, or 0 if there is none". Both forms are fine for reading. The default: form has an extra use for writing, as in counts[word, default: 0] += 1, which counts words in one line. That is for a later lesson, but it explains why the form is worth knowing.

    Choosing between default: and ??

    For reading, pick whichever reads better in your line. stock["plums", default: 0] says the fallback belongs to the lookup. stock["plums"] ?? 0 says "if there is no value, use 0", and it also works on any other optional, not just dictionary lookups. If you need to know whether the key was there at all, use neither: compare the plain lookup with nil instead.

    Why the default matters for writing

    The same subscript also works on the left of an assignment. Counting how often each word appears is the classic case:

    var counts: [String: Int] = [:]
    for word in ["red", "blue", "red"] {
        counts[word, default: 0] += 1
    }
    print(counts["red", default: 0], counts["blue", default: 0], counts["green", default: 0])
    
    2 1 0
    

    The first time a word shows up, the default 0 is used, and 1 is stored. Without the default, a missing key would give nil and the addition would not compile. This is the same idea as the lesson's exercise, in the other direction.

    Traps

    • Choosing a sensible default. It must make sense for the question. 0 fits a count. For a name, "" may fit better.
    • A read does not store the default. After stock["plums", default: 0], the count is still 1, and stock["plums"] == nil is still true.
    • Keys must match exactly, letter case included. stock["Plums", default: 0] looks for a different key.

    The exercise: counting fruit

    stock counts the fruit in a shop. The starter code reads and prints the pears. The pears exist, so the plain lookup with ! is safe there:

    let stock: [String: Int] = ["apples": 5, "pears": 2]
    let pears = stock["pears"]!
    print("We have " + String(pears) + " pears")
    

    There are no plums. Read "plums" with default: 0 and store it in plums. Then print the plums line the same way as the pears line, with String(plums) turning the number into text:

    let plums = stock["plums", default: 0]
    print("We have " + String(plums) + " plums")
    

    The whole program prints:

    We have 2 pears
    We have 0 plums
    

    If you had used stock["plums"]! here, the program would have crashed on the missing key before it printed the line.

    Key takeaways

    • dict[key] is optional. dict[key, default: d] is a plain value.
    • The default is returned for a missing key and is not stored.
    • dict[key] ?? d is another way to read with a fallback.
    Keep going with Swift500+ step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free