Getting the item and its count
A loop such as for i in 0..<meals.count gives you an index, and then meals[i] gives the item. Swift has a shorter way to get both at once. enumerated() gives each item together with a count, as a tuple, and the loop unpacks that tuple into two names:
let fruits = ["apple", "kiwi", "plum"]
for (i, fruit) in fruits.enumerated() {
print("\(i) \(fruit)")
}
0 apple
1 kiwi
2 plum
The count comes first, then the item. It starts at 0, so print i + 1 when you want a list numbered from 1. The unpacking works exactly like let (name, age) = pet on a tuple.
If you prefer not to unpack, each item is a tuple with the labels offset and element: for pair in fruits.enumerated() lets you write pair.offset and pair.element. Unpacking into your own names is usually clearer, because (i, fruit) says what each part is.
Walking two arrays side by side
zip(a, b) pairs the first item of a with the first item of b, then the second items, and so on. Each pair is a tuple, and the loop unpacks it:
let prices = [3, 1, 2]
for (fruit, price) in zip(fruits, prices) {
print("\(fruit) costs \(price)")
}
apple costs 3
kiwi costs 1
plum costs 2
Two details trip people up:
zipis a function, not a method. Writezip(fruits, prices).fruits.zip(prices)fails with "value of type '[String]' has no member 'zip'".zipstops at the shorter array. The extra items of the longer one are skipped, with no error.zip(fruits, [3, 1])gives only the pairs for apple and kiwi.
To see what zip builds, turn it into an array: Array(zip(fruits, prices)) is [("apple", 3), ("kiwi", 1), ("plum", 2)], a list of tuples. The loop simply unpacks one tuple per pass. Compared with an index loop, there is no prices[i] to get wrong, and no risk of running past the end of the shorter array.
Using both together
The two work together. zip(fruits, prices).enumerated() gives a count and a pair at each step, and the names nest the same way:
for (i, (fruit, price)) in zip(fruits, prices).enumerated() {
print("\(i + 1). \(fruit) costs \(price)")
}
1. apple costs 3
2. kiwi costs 1
3. plum costs 2
The inner parentheses match the inner tuple: the count is outside, and the pair is inside.
The lesson's exercise
The array players holds names, and scores holds their points in the same order.
- Numbered list. Loop over
players.enumerated(), unpacking each item into(i, player), and print"\(i + 1). \(player)". The count starts at 0, so adding 1 makes Ana number 1. - Players with points. Loop over
zip(players, scores), unpacking each pair into(player, points), and print"\(player) has \(points) points". No index is needed, becausezipalready matched Ana with 12, Ben with 9 and Cleo with 15.
class Solution {
func mainFunction() {
let players = ["Ana", "Ben", "Cleo"]
let scores = [12, 9, 15]
for (i, player) in players.enumerated() {
print("\(i + 1). \(player)")
}
for (player, points) in zip(players, scores) {
print("\(player) has \(points) points")
}
}
}
The output is:
1. Ana
2. Ben
3. Cleo
Ana has 12 points
Ben has 9 points
Cleo has 15 points
Common mistakes
- Names in the wrong order. In
enumerated()the count comes first.for (player, i) in ...would put the count intoplayer. - Forgetting the + 1. A list numbered from 0 looks like a mistake to the reader and to the lesson's tests.
- Forgetting the second pair of parentheses when combining the two.
for (i, fruit, price) in ...is a compile error, because the items are a count and a pair, not three values.
Key takeaways
enumerated()gives(count, item), counting from 0.zip(a, b)gives pairs(itemFromA, itemFromB)and stops at the shorter array.- Nest the tuple names to combine them:
(i, (a, b)).
