What a dictionary loop does
A dictionary stores entries, each one a key with its value. A for-in loop over a dictionary runs its body once per entry. Swift hands the loop each entry as a pair, so you can split it into two names right in the loop header:
let stock = ["apples": 5, "pears": 2]
for (fruit, amount) in stock {
print(fruit, amount)
}
The key always comes first and the value second. With this dictionary the loop prints these two lines, in either order:
apples 5
pears 2
Under the hood, each entry is a tuple labelled (key: String, value: Int). The pattern (fruit, amount) takes it apart. If you prefer, write for entry in stock and use entry.key and entry.value.
Only the keys, or only the values
To visit just the keys, loop over stock.keys. To visit just the values, loop over stock.values. When you do want the pair but not one half of it, write _ in its place:
for (_, amount) in stock {
print(amount)
}
The underscore says "I don't need this part". It matters because Swift warns about a name you declare and never use, and _ avoids that warning.
The order is not fixed
A Swift dictionary has no fixed order, and the order can differ from one run of the program to the next. So code that depends on which entry comes first is wrong, even if it seemed to work once. Two kinds of answer are safe in any order:
- a total, built by adding every value;
- a count of the entries that pass a test.
If you do need a stable order, sort first. stock.sorted(by: { $0.key < $1.key }) gives an array of entries ordered by key, and looping over that array prints apples 5 and then pears 2 every time. Sorting is not needed for this lesson.
The lesson's exercise
The code starts with these lines:
let scores: [String: Int] = ["Ana": 12, "Ben": 7, "Cleo": 15, "Dan": 9]
var total = 0
var count = 0
The task is to add up all the points in total and count in count the players with 10 points or more. The names don't matter, so the loop header splits each entry into (_, points):
for (_, points) in scores {
total += points
if points >= 10 {
count += 1
}
}
Step by step: the loop gives points the value of one player each time. The first line of the body adds it to total. The if adds 1 to count only when that player has 10 or more. The points are 12, 7, 15 and 9, so the total is 43. Ana (12) and Cleo (15) reach 10, so the count is 2. The two print calls at the bottom of the code give:
43
2
Because the answers are a sum and a count, the result is the same whichever order Swift uses.
Common mistakes
- Swapping the names. In
(points, name)the first name would hold the key, sopointswould be aStringandtotal += pointswould not compile. The key is first. - Relying on the order. A loop that keeps "the first player it sees" gives different answers on different runs.
- Changing the dictionary while looping over it. The loop works on a copy of the dictionary as it was when the loop began, so changes made inside the body don't affect which entries are visited. Build the changes in a separate variable instead.
- Forgetting
varon the totals.totalandcountchange inside the loop, so they must be declared withvar. Aletwould be a compile error.
Key takeaways
for (key, value) in dictvisits every entry once._skips the half you don't need.dict.keysanddict.valuesloop over one half only.- The order is not fixed, so use totals and counts, or sort first.
