Iterate over items and indices in collections
Iterating over both the items and their indices in a collection is a common requirement in Swift programming. While the enumerated()
method might seem like the obvious choice for this task, it's crucial to understand its limitations. The integer it produces starts at zero and increments by one for each item, which is perfect for use as a counter but not necessarily as an index, especially if the collection isn't zero-based or directly indexed by integers.
Here's a typical example using enumerated()
.
var ingredients = ["potatoes", "cheese", "cream"]
for (i, ingredient) in ingredients.enumerated() {
// The counter helps us display the sequence number, not the index
print("ingredient number \(i + 1) is \(ingredient)")
}
For a more accurate way to handle indices, especially when working with collections that might be subsets or have non-standard indexing, we can use the zip()
function. This method pairs each element with its actual index, even if the collection has been modified.
// Array<String>
var ingredients = ["potatoes", "cheese", "cream"]
// Array<String>.SubSequence
var doubleIngredients = ingredients.dropFirst()
for (i, ingredient) in zip(
doubleIngredients.indices, doubleIngredients
) {
// Correctly use the actual indices of the subsequence
doubleIngredients[i] = "\(ingredient) x 2"
}
This approach ensures that we are using the correct indices corresponding to the actual positions in the modified collection.
For a better interface over zip()
, we can also use the indexed()
method from Swift Algorithms. It's equivalent to zip(doubleIngredients.indices, doubleIngredients)
but might be more clear.
import Algorithms
// Array<String>
var ingredients = ["potatoes", "cheese", "cream"]
// Array<String>.SubSequence
var doubleIngredients = ingredients.dropFirst()
for (i, ingredient) in doubleIngredients.indexed() {
// Do something with the index
doubleIngredients[i] = "\(ingredient) x 2"
}