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

Looping over a dictionary

Let's go back to the phone book. Sometimes you need not one number but the whole book at once, for example to print every contact. You would leaf through a paper book page by page and read it line by line. You can go through a whole dictionary too, but with one caveat about order.

Looping over pairs

A for-in loop walks through a dictionary pair by pair. Each pair is a tuple of a key and a value, and the loop header splits it into two names right away:

SWIFT
let phones = ["Anna": "555-1234", "Boris": "555-9876", "Vera": "555-0000"]
for (name, phone) in phones {
    print(name, phone)
}

Let's go through the program line by line:

  1. The first line creates a dictionary of three pairs: the key is a name, the value is a number.
  2. for (name, phone) in phones takes one pair on each iteration: the key goes into name, the value into phone.
  3. print(name, phone) prints the name and the number separated by a space.

The program prints three lines, one per pair. In what order is not known in advance. Run it two or three times, and the lines may come out in another order: on one run Anna comes first, on the next one Boris.

This is the flaw in the picture mentioned in the previous lesson: in a paper book the names go in alphabetical order. A dictionary lays out its pairs so that it finds a key fast, and Swift picks that layout anew on every run of the program. That is why a program must never rely on the order of pairs in a dictionary.

If the loop doesn't need the name or the number, write _ in its place: for (_, phone) in phones.

Keys in alphabetical order

When you need an order, you set it yourself. phones.keys is all the keys of the dictionary, and sorted() returns them as an array in ascending order. Strings then come in alphabetical order, except that capital Latin letters come before lowercase ones:

SWIFT
for name in phones.keys.sorted() {
    print(name, terminator: " ") // Anna Boris Vera
}

Now the names come in alphabetical order on every run: the loop goes over the sorted array, not over the dictionary itself. The number for each name comes from a lookup: phones[name] ?? "none". All the values of a dictionary, without the keys, come from phones.values, also in no order.

Common mistake

The number is looked up, but the box is not opened:

SWIFT
for name in phones.keys.sorted() {
    print(name, phones[name]) // ✗
}
TEXT
warning: expression implicitly coerced from 'String?' to 'Any'

This is a warning, so the program runs. But instead of Anna 555-1234 the loop prints Anna Optional("555-1234"), and so on in every line. The name comes from the keys of the same dictionary, but the compiler doesn't take that into account: a lookup by key always gives an optional. Open the box: phones[name] ?? "none".

Key points

  • for (key, value) in dict goes over the pairs, but their order is not fixed and may change from run to run.
  • When you need an order, loop over dict.keys.sorted() and get each value by a lookup.
  • A lookup gives an optional even when the key comes from the same dictionary: open it with ??.