Skip to content
Theory · Dictionaries and sets · lesson 3 of 6

Counting with a dictionary

A common task is to count how many times each item occurs: a word in a text or a name in a call log. The phone book helps with such counting again.

Picture a call log: Anna, Boris, Anna again. To find out who called how many times, keep a book where each name has not a number next to it but a count of calls. Meet a name in the log, find its line and add one. If the name isn't in the book yet, write it in with the count 1.

In Swift such a book is a [String: Int] dictionary: what you count serves as the key, and the counter as the value. Here the book picture is inaccurate. On paper you count a name that isn't in the book as zero calls. But in Swift a lookup of a missing key gives nil, and you can't add one to it.

Lookup with a fallback value

For this case a dictionary has a lookup with a fallback value. The form counts[name, default: 0] reads as "the value for the key name, or 0 if there is no such key". Here default: is an argument label, not a switch branch. The result of such a lookup is a plain Int, with no question mark:

SWIFT
let counts = ["Anna": 2, "Boris": 1]
print(counts["Anna", default: 0]) // 2
print(counts["Gleb", default: 0]) // 0

Let's go through the program line by line:

  1. The first line creates a dictionary of call counters: Anna called twice, Boris once.
  2. The key "Anna" is there, and the lookup gives the stored value 2.
  3. There is no key "Gleb", and the lookup gives the fallback value 0.

For reading, this form works like counts["Gleb"] ?? 0. The difference shows on a write: through default: you can store a new value in the dictionary, and through ?? you can't.

Counting in a loop

The statement callCount[name, default: 0] += 1 takes three steps:

  1. It finds the value for the key, or takes 0 if there is no such key.
  2. It adds one to it.
  3. It stores the result under the same key. If the key was missing, a new pair appears in the dictionary.

Press "Next step" and watch the dictionary callCount after each run of line 4. The widget writes the pairs in the order they appeared, while a real dictionary keeps no order.

LiveCounting callsstep 1 of 11 · frames: 1

The log holds three calls: Anna called twice, Boris once.

Watch how the dictionary callCount changes after line 4

Look at the last steps: a read with default: does not add the key. After callCount["Gleb", default: 0] the dictionary still has two pairs. A pair appears only when something is written through default:, as in += 1.

"Count how many times each word occurs" is one of the most common interview tasks. In Swift it is solved with a [String: Int] dictionary and a loop with counts[word, default: 0] += 1.

Common mistake

The counter is increased without default::

SWIFT
var callCount: [String: Int] = [:]
callCount["Anna"] += 1 // ✗
TEXT
error: value of optional type 'Int?' must be unwrapped to a value of type 'Int'

The program does not compile: callCount["Anna"] has the type Int?, and += can add a number to an Int but not to an Int?. There is no key "Anna" yet, so there is nothing to add one to. A fallback value solves it: callCount["Anna", default: 0] += 1.

Key points

  • dict[key, default: 0] gives a plain value, and += 1 through it stores the pair if it was missing.
  • A read with default: does not change the dictionary: a pair appears only on a write.
  • Without default: a lookup gives an Int?, and += 1 on it does not compile.