Do you need help on a specific subject? Use the contact form (Request a blog entry) on the right hand side.

2015-01-28

Array's and for loops

At first glance, Swift offers a bewildering number of possibilities to iterate over an array in a for loop. The ones below are all the variations I could come up with.

This is probably the most used and simplest way to do this:

let values: Array<String> = ["one", "two", "three", "four"]

for value in values {
    println("value: \(value)")
}

Outputs:
value: one
value: two
value: three
value: four


In order to reverse the direction we can use the 'reverse' function:

for value in reverse(values) {
   println("value: \(value)")
}

Outputs:
value: four
value: three
value: two
value: one

So far, so good, but what if we also need the index within the loop? Well, Swift gives us the 'enumerate' function:

for (index, value) in enumerate(values) {
    println("index: \(index), value: \(value)")
}

Outputs:
index: 0, value: one
index: 1, value: two
index: 2, value: three
index: 3, value: four


And can we stack the reverse on the enumerate? Yes we can, but with a gotcha:

for (index, value) in enumerate(reverse(values)) {
    println("index: \(index), value: \(value)")
}

Outputs:
index: 0, value: four
index: 1, value: three
index: 2, value: two
index: 3, value: one


See the gotcha? the index does not reverse, it still counts from 0 to count-1! This is probably because the enumerate function creates the index, not the reverse function. As far as the enumerate is concerned, it still counts up. Note that it is not possible to call the reverse on the result of the enumerate function. (i.e. reverse(enumerate(values)) does not compile)


When we need the index to reverse as well, we can use the old model for loop:

for var index = values.count; index > 0; {
    let value = values[--index]
    println("index: \(index), value: \(value)")
}

Outputs:
index: 3, value: four
index: 2, value: three
index: 1, value: two
index: 0, value: one

Note that in the above the ';' before the loop is necessary to create an empty statement before the loop starts. Also note that the first use of the index must have the '--' operator in front of it, and this is in fact part of the loop maintenance. I find this a bit ugly, but it does work. We can also write this as follows:

for var index = values.count - 1; index >= 0; --index {
    let value = values[index]
    println("index: \(index), value: \(value)")
}

Outputs:
index: 3, value: four
index: 2, value: three
index: 1, value: two
index: 0, value: one

but personally I find this code even uglier.

I would rather use the newer methods:

let values: Array<String> = ["one", "two", "three", "four", "five"]

for index in 0 ..< values.count {
    let value = values[index]
    println("index: \(index), value: \(value)")
}

Outputs:
index: 0, value: one
index: 1, value: two
index: 2, value: three
index: 3, value: four
index: 4, value: five

or:

for index in 0 ... values.count - 1 {
    let value = values[index]
    println("index: \(index), value: \(value)")
}

Outputs:
index: 0, value: one
index: 1, value: two
index: 2, value: three
index: 3, value: four
index: 4, value: five

These can also be used with the reverse function:

for index in reverse(0 ..< values.count) {
    let value = values[index]
    println("index: \(index), value: \(value)")
}

Outputs:
index: 4, value: five
index: 3, value: four
index: 2, value: three
index: 1, value: two
index: 0, value: one

Note that this index does count down!


There are also the 'stride:from:to:by' and 'stride:from:through:by' functions which can be used to step in other increment than 1. These can also be used to step through in the reverse direction by specifying a negative value for the 'by' parameter.

let values: Array<String> = ["one", "two", "three", "four", "five"]

for index in stride(from:0, to:values.count - 1, by:2) {
    let value = values[index]
    println("index: \(index), value: \(value)")
}

Outputs:
index: 0, value: one
index: 2, value: three


for index in stride(from:0, through:values.count - 1, by:2) {
    let value = values[index]
    println("index: \(index), value: \(value)")
}

Outputs:
index: 0, value: one
index: 2, value: three
index: 4, value: five

The 'through' variant is inclusive, the 'to' variant is exclusive, similar to '...' and '..<'

Quite a lot of possibilities, which one you use will of course depend on what you need. In general I would avoid the old syntax, it just does not seem to offer any advantage when iterating over an array.
At the end, I leave you with a list of all the possibilities presented above, first the forward loops, then the reverse loops (all in steps of 1 or -1 and all loop over the entire array):

for value in values { ... }
for (index, value) in enumerate(values) { ... }
for index in 0 ..< values.count { let value = values[index]; ... }
for index in 0 ... values.count - 1 { let value = values[index]; ... }
for index in stride(from:0, to:values.count, by:1) { let value = values[index]; ... }
for index in stride(from:0, through:values.count-1, by:1) { let value = values[index]; ... }
for var index = 0; index < values.count; index++ { let value = values[index]; ... }

for value in reverse(values) { ... }
for (index, value) in enumerate(reverse(values)) { ... }  // index does NOT reverse!
for index in reverse(0 ..< values.count) { let value = values[index]; ... }
for index in stride(from:values.count - 1, to:-1, by:-1) { let value = values[index]; ... }
for index in stride(from:values.count - 1, through:0, by:-1) { let value = values[index]; ... }
for var index = values.count; index > 0; { let value = values[--index]; ... }
for var index = values.count - 1; index >= 0; --index { let value = values[index]; ... }

Happy coding...

Did this help?, then please help out a small independent.
If you decide that you want to make a small donation, you can do so by clicking this
link: a cup of coffee ($2) or use the popup on the right hand side for different amounts.
Payments will be processed by PayPal, receiver will be sales at balancingrock dot nl
Bitcoins will be gladly accepted at: 1GacSREBxPy1yskLMc9de2nofNv2SNdwqH

We don't get the world we wish for... we get the world we pay for.

No comments:

Post a Comment