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:
Student | Grade |
---|---|
Alice | 20 |
Charlie | 14 |
Bob | 7 |
This table records a grade for each student.
You can store this information in an array of tuples:
[("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:
var grades = ["Alice": 20, "Bob": 7, "Charlie": 14]
An empty dictionary also includes a colon to differentiate it from an empty array:
grades = [:]
Of course, you must include a type annotation when you declare an empty dictionary:
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:
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:
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
:
grades["David", default: 0]
This is similar to using the nil-coalescing operator:
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:
credits["Alice", default: 0] += 1
This is equivalent to:
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:
grades["Eric"] = 10
Conversely, you can remove a key by setting its value to nil
:
grades["Eric"] = nil
Properties and methods
Array
and Dictionary
are related types that offer many of the same properties and methods:
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:
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:
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:
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:
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:
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.