In 2015 I wrote a short Russian post about getting an element’s index while looping over an array in Swift. The answer back then was the global enumerate(array), which Swift 2 turned into the method array.enumerate(). Swift is now at 6.3, the method is called enumerated(), and the old post missed the important part: enumerated() doesn’t give you indices.
The easy case
1 | let array = ["zero", "one", "two", "three"] |
For an Array starting at 0, the numbers are also valid indices, so this works and it’s what most code does.
Where it breaks
enumerated() counts from zero, whatever the collection is. The numbers are offsets. Take a slice:
1 | let tail = array[2...] // ArraySlice, indices 2 and 3 |
A slice keeps its parent’s indices, so tail[0] crashes. If you use the i from enumerated() to index back into a slice, the code works in tests on full arrays and crashes on the first slice.
Indices, done properly
Pair each element with its real index:
1 | for (i, value) in zip(tail.indices, tail) { |
Or loop over the indices directly when you need to subscript:
1 | for i in tail.indices { |
zip(c.indices, c) works for any collection, including those whose indices aren’t integers at all, like String:
1 | let s = "héllo" |
The rule
- Want a counter (“row 1, row 2…”)? Use
enumerated(). - Want to subscript the collection? Use
indicesorzip(c.indices, c).
All snippets checked with Swift 6.3.1.