# How to loop over arrays

Paul Hudson       @twostraws

I know what you’re thinking: looping over arrays is trivial, so why should I read an article about it?

Well, it turns out there are several ways of looping over arrays and other collections, and it’s worth recapping them – hopefully you’ll find something new along the way.

First, let’s create a simple collection we can work with: a dictionary containing the names of various science fiction shows, plus a starships from each one:

``````let ships = [
"Star Trek": "Enterprise",
"Firefly": "Serenity",
"Aliens": "Sulaco"
]``````

The standard, go-to approach to looping over that dictionary is like this:

``````for ship in ships {
print("\(ship.value) is from \(ship.key)")
}``````

Because dictionaries are collections, just like arrays and sets, you can go over them in reverse:

``````for ship in ships.reversed() {
print("\(ship.value) is from \(ship.key)")
}``````

And you can use `enumerated()` to get back their position and value in one go:

``````for (index, ship) in ships.enumerated() {
print("\(index + 1). \(ship.value) is from \(ship.key)")
}``````

You can even combine `enumerated()` and `reversed()`, but be careful: `enumerated().reversed()` is not the same as `reversed().enumerated()`:

``````// this will count down from 3 to 1
for (index, ship) in ships.enumerated().reversed() {
print("\(index + 1). \(ship.value) is from \(ship.key)")
}

// this will count up from 1 to 3
for (index, ship) in ships.reversed().enumerated() {
print("\(index + 1). \(ship.value) is from \(ship.key)")
}``````

Finally, you will occasionally see the `forEach()` method being used, which does much the same as a regular `for-in` loop except in a functional style:

``````ships.forEach {
print("\(\$0.value) is from \(\$0.key)")
}``````

For `forEach()` method doesn’t do anything special. In fact, if you take a peek at the Swift source code you’ll see it literally just becomes a `for-in` loop:

``````public func forEach(_ body: (Element) throws -> Void) rethrows {
for element in self {
try body(element)
}
}``````

That doesn’t mean it’s useless, though: using `forEach()` does guarantee your closure will be run on every item in the collection (there are no surprise `break` or `return` statements in there!).

Using `forEach()` is also nice when you want to call a function with each value. For example, you might write a function to calculate factors, like this:

``````func calculateFactors(_ number: Int) {
let factors = (1...number).filter { number % \$0 == 0 }
let stringFactors = factors.map { String(\$0) }
let joinedFactors = stringFactors.joined(separator: ", ")
print("Factors of \(number): \(joinedFactors)")
}``````

You can then call that directly using `forEach()`:

``(1...100).forEach(calculateFactors)``

### What are you trying to achieve?

Previously I’ve talked about the importance of writing code that conveys your intent, which is why I often recommend `map()` and `compactMap()` when transforming array items.

The same lesson applies to looping: when you write a loop, it’s important to consider what you’re actually trying to achieve, because Swift can often provide alternatives that are clearer and perhaps even safer too.

The most common problem people face is looping over array indices, like this:

``````let movies = [
"The Phantom Menace",
"Attack of the Clones",
"Revenge of the Sith",
"A New Hope",
"The Empire Strikes Back",
"Return of the Jedi"
]

for i in 0 ..< movies.count {
print("Episode \(i + 1): \(movies[i])")
}``````

That will print out the numbers and names of the first six Star Wars movies. Of course, any Star Wars fan will know how bad the Phantom Menace was, so they’d probably write this:

``````for i in 1 ..< movies.count {
print("Episode \(i + 1): \(movies[i])")
}``````

And real Star Wars fans would write this:

``````for i in 3 ..< movies.count {
print("Episode \(i + 1): \(movies[i])")
}``````

But both of those code examples contain a possible problem: if your `movies` array is empty, or lower than your start position, then your code will crash at runtime because creating a range where the start is higher than the end isn’t allowed.

The smarter thing to do is ditch using the index counter entirely by relying on `dropFirst()` instead. Called with no parameters this will drop the single first item, and called with a parameter will drop that many items.

For example:

``````for movie in movies.dropFirst() {
print("Star Wars: \(movie)")
}

for movie in movies.dropFirst(3) {
print("Star Wars: \(movie)")
}``````

If you call `dropFirst()` on an empty array or try to drop more items than you have, it won’t cause a crash – you’ll just get back an empty subsequence.

You can of course combine this with `enumerated()` or `reversed()`, like this:

``````for (index, movie) in movies.dropFirst(3).enumerated() {
print("Episode \(index + 4): \(movie)")
}``````

What matters here is that your intent is clear: you want to loop over the array, skipping the first items.

And if you really do want to count from zero up to the size of an array, you should use the `indices` property instead:

``````for i in movies.indices {
print("Episode \(i + 1): \(movies[i])")
}``````

Compare these two lines of code:

``````for i in 0 ..< movies.count {
for i in movies.indices {``````

I know the second option is a little shorter, but it also clarifies your intent – you’re not looping from one arbitrary value to another, but instead going over each index inside the array.

Again, this is so similar to `map()`. I often quote Javier Soto, who said “`map()` allows us to express what we want to achieve, rather than how this is implemented.” The same is true of `indices`: rather than explicitly creating a range by hand, we’re relying on Swift to figure that out.

As with the other approaches, `indices` can be used with `reversed()` if you want to loop backwards through a collection:

``for i in movies.indices.reversed() {``

That kind of code is so much easier to read than the equivalent where you specify the limits by hand:

``````for i in (0 ..< movies.count).reversed() {
print(i)
}``````

As you’ve seen there are lots of ways you can loop over collections. You won’t use them all at the same time, but hopefully you’ve seen how each variant has its own uses.

HACKING WITH SWIFT LIVE This July is a new two-day event where you'll be inspired by great speakers on day one then learn all the amazing new features from WWDC on day two – click here for more information and tickets.

MASTER SWIFT NOW