Skip to content

Dictionaries

In the previous chapter, you learned how you can store a collection of values in an array. Arrays order their elements and store them together in memory. Unfortunately, this ordering comes with a cost, so an array isn’t always the best tool for the job.

This chapter introduces a second collection type, dictionaries, and compares it to arrays.

Let’s start with an example:

StudentGrade
Alice20
Charlie14
Bob7

This table records a grade for each student.

You can store this information in an array of tuples:

swift
[("Alice", 20), ("Charlie", 14), ("Bob", 7)]

The order in which you store these tuples isn’t essential here, only that you record the correct grade for each student. Therefore, you don’t need an ordered collection, and you should consider alternatives — in this case, a dictionary.

Keys and values

A dictionary is an unordered collection; it can store its elements in any order it wants. As a result, the elements in a dictionary don’t have a fixed index. Instead, a dictionary uses keys to identify its elements.

Each element in a dictionary consists of a key and a value. In the example above, the students are the keys, and the grades are the values. Keys must be unique, so you can’t have two grades for the same student.

You don’t think about indices when you use a dictionary. You look up the value for a particular key — such as the grade for Bob — not the value at a specific index. This is similar to how you use a dictionary to look up the definition of a word. This analogy is where dictionaries get their name.

Declaration

The simplest way to declare a dictionary is to use a dictionary literal. This literal is similar to an array literal but lists pairs of keys and values, separated by a colon:

swift
var grades = ["Alice": 20, "Bob": 7, "Charlie": 14]

An empty dictionary also includes a colon to differentiate it from an empty array:

swift
grades = [:]

Of course, you must include a type annotation when you declare an empty dictionary:

swift
var credits: [String: Int] = [:]

The next section explains this type.

Types

Like arrays, dictionaries are homogeneous collections. The keys in a dictionary must be of a single type, and the values must be of a single type as well. Both grades and credits have keys of type String and values of type Int.

The type of these dictionaries is [String: Int], a dictionary with strings as keys and integers as values. The full name for this type is Dictionary<String, Int>, which tells you that there’s a single type Dictionary that powers all dictionaries.

Subscripts

After you’ve declared a dictionary, you’ll mostly use subscripts to work with it. Instead of an index, you specify a key as a subscript to access or mutate the value for that key:

swift
grades["Alice"]
grades["Alice"] = 20

Of course, you can only mutate a variable dictionary, not a constant one.

Unlike arrays, whose indices range from zero to one less than count, dictionaries cannot guarantee that they contain a particular key. Therefore, a dictionary returns its values as optionals.

Use optional binding when you’re not sure if a particular key is in the dictionary:

swift
if let grade = grades["David"] {
  print("David has a grade of \(grade).")
} else {
  print("David doesn’t have a grade yet.")
}

Alternatively, you can provide a default value to use in place of nil:

swift
grades["David", default: 0]

This is similar to using the nil-coalescing operator:

swift
grades["David"] ?? 0

However, the benefit of using a subscript is that it can also mutate a value. For example, here’s how you can increase the number of credits for Alice:

swift
credits["Alice", default: 0] += 1

This is equivalent to:

swift
credits["Alice"] = credits["Alice"] ?? 0 + 1

This example also shows that you can add a new key to the dictionary by assigning it a value:

swift
grades["Eric"] = 10

Conversely, you can remove a key by setting its value to nil:

swift
grades["Eric"] = nil

Properties and methods

Array and Dictionary are related types that offer many of the same properties and methods:

swift
grades.count
credits.isEmpty

As you would expect, count returns the number of key/value pairs in the dictionary, and isEmpty returns true if the dictionary is empty.

Dictionary also has properties and methods that are specific to it. One such method is updateValue(_:forKey:). This method assigns a new value to a key and returns the old value for that key, or nil if the key wasn’t in the dictionary:

swift
if let oldGrade = grades.updateValue(14, forKey: "David") {
  print("David’s grade was \(oldGrade) and is now 14.")
} else {
  print("David’s grade is now 14.")
}

You can discover more properties and methods by exploring the Standard Library documentation.

Iteration

To iterate over a dictionary, you use a for-in loop with the tuple pattern. This is similar to using the enumerated method on arrays:

swift
for (student, grade) in grades {
  print("\(student) has grade \(grade).")
}

Keep in mind that dictionaries are unordered, so you cannot rely on the order in which the elements are returned; this is considered an implementation detail.

If you want a consistent order, you can create a sorted copy of the keys property and use that to iterate over the dictionary:

swift
for student in grades.keys.sorted() {
  print("\(student) has grade \(grades[student]!).")
}

This prints grades in alphabetical order.

Dictionary also has a sorted(by:) method that provides more sorting options. You’ll learn how to use this method in the next chapter.

Operators

You can use the equality operator (==) to test if two dictionaries contain the same keys and have the same value for each of those keys:

swift
credits == ["Alice": 1]

This only works if the equality operator knows how to compare the keys and values in the dictionaries. In this case, == can compare strings and integers, so it can also compare dictionaries that use these types.

Arrays vs. dictionaries

Now that you’ve learned about arrays and dictionaries, you might be wondering why you need both. After all, you can create a dictionary with integer keys, which works much like an array:

swift
var boardArray = ["X", "O", "X",
                  "O", "X", "O",
                  "O", "X", "X"]
var boardDictionary = [0: "X", 1: "O", 2: "X",
                       3: "O", 4: "X", 5: "O",
                       6: "O", 7: "X", 8: "X"]
boardArray[0]
boardDictionary[0]

What’s the difference between boardArray and boardDictionary, and which one should you prefer? To answer these questions, you need to take a deeper look at the operations you’ll perform on these collections.

Reading an element

Both collections perform equally well at looking up elements. The difference is how elements are returned: dictionaries use optionals, whereas arrays don’t.

The reasoning behind this is that the indices of an array form a range. Any index between zero and one less than count is valid; any index outside of this range is not. It’s your responsibility as the programmer to only use an index that falls within this range. The array is then guaranteed to have a value for that index. Dictionaries have no such guarantee. The only way to know if a dictionary contains a key is to look up the value for that key. This is why dictionaries return optionals.

Conclusion: If you’re using integers as keys and those keys form a range, you should favor an array. If not, a dictionary is the better option.

Adding and removing elements

Arrays are designed to grow or shrink at the back. If you insert an element at an existing index, the array has to move some of its elements backward to make room for the new element. Likewise, if you remove an element from an array, the array has to move some of its elements forward to close the gap. This makes insertion and removal potentially expensive operations.

Dictionaries, on the other hand, are unordered, so it doesn’t matter where you add or remove elements.

Conclusion: If your algorithm only adds and removes elements at the back of the collection, an array is a good choice. Otherwise, a dictionary will perform better.

Iterating over the collection

Both collections support fast iteration. However, iterating over a dictionary in sorted order requires an additional sort operation, which is expensive.

Conclusion: If the order of the elements is important, an array should be your first choice. If you rarely need to sort the collection, a dictionary is acceptable too.

Conclusion

To wrap up this comparison, let’s discuss the case of boardArray vs. boardDictionary:

  • The order of the elements is important because they form rows, columns, and diagonals.
  • You can represent the positions on the board as a range of integers: 0 to 8. You can also represent them as strings: "top left", "top center", and so on, but that makes them harder to order.
  • The size of the board is fixed. You’ll never add or remove elements.

For these reasons, an array is the correct choice to store the Tic-Tac-Toe board.

Up next

The next chapter discusses some of the advanced methods that Array and Dictionary have to offer, and the language features that power them.

But first, test your understanding of arrays and dictionaries by completing the upcoming exercises.