VYT
VYT

Reputation: 1071

How to write a non-C-like for-loop in Swift 2.2+?

I have updated Xcode (7.3) and there are a lot of changes; C-like for expressions will be deprecated. For a simple example,

for var i = 0;  i <= array.count - 1;  i++
 {
      //something with array[i]
 } 

How do I write this clear and simple C-like for-loop to be compliant with the new changes?

for var i = 0, j = 1;  i <= array.count - 2 && j <= array.count - 1;  i++, j++
{
     //something with array[i] and array[j]
}  

Update. One more variant

for var i = 0; i <= <array.count - 1; i++
{
    for var j = i + 1; j <= array.count - 1; j++
    {
        //something with array[i] and array[j]
    }
}

And more ...

for var i = 0, j = 1, g = 2;  i <= array.count - 3 && j <= array.count - 2 &&   g <= array.count - 1;  i++, j++, g++
{
     //something with array[i] and array[j] and array[g]
} 

Update2 After several suggestions for me while loop is preferable universal substitution for all cases more complicated than the simple example of C-like for-loop (suitable for for in expression). No need every time to search for new approach.
For instance: Instead of

for var i = 0; i <= <array.count - 1; i++
{
    for var j = i + 1; j <= array.count - 1; j++
    {
        //something with array[i] and array[j]
    }
}

I can use

var i = 0
while i < array.count
{
    var j = i + 1
    while j < array.count
    {
        //something with array[i] and array[j]
        j += 1
    }
    i += 1
}

Upvotes: 8

Views: 2193

Answers (7)

JAL
JAL

Reputation: 42449

charl's (old) answer will crash. You want 0..<array.count:

for index in 0..<array.count {
    // ...
}

If you want something like your i/j loop you can use stride and get i's successor:

for i in 0.stride(through: array.count, by: 1) {
    let j = i.successor()
    // ...
}

Just make sure to check i.successor() in case you go out of bounds.

Upvotes: 8

user3441734
user3441734

Reputation: 17544

to compare neighbouring elements from the same array you can use

let arr = [1,2,2,5,2,2,3,3]
arr.reduce(nil) { (i, j)->Int? in
    if let i = i {
        print(i,"==",j,"is",i == j)
    }
    return j
}

it prints

1 == 2 is false
2 == 2 is true
2 == 5 is false
5 == 2 is false
2 == 2 is true
2 == 3 is false
3 == 3 is true

more 'generic' approach without using subscript but separate generators

let arr1 = [1,2,3,4,5,6,7,8,9,0]

var g1 = arr1.generate()
var g2 = (arr1.dropFirst(5) as AnySequence).generate()
var g3 = (arr1.dropFirst(6) as AnySequence).generate()

while true {
    if let a1 = g1.next(),
        let a2 = g2.next(),
        let a3 = g3.next() {
        print(a1,a2,a3)
    } else {
        break
    }
}
/* prints
 1 6 7
 2 7 8
 3 8 9
 4 9 0
 */

Upvotes: 1

Daniel T.
Daniel T.

Reputation: 33967

Worst case, you can convert it to a while loop.

var i = 0
var j = 1
while i <= array.count -2 && j <= array.count - 1 {
    // something
    i += 1
    j += 1
}

-- EDIT --

Because you said, "while loop is preferable universal substitution for all cases more complicated than the simple example of C-like for-loop"... I feel the need to expand on my answer. I don't want to be responsible for a bunch of bad code...

In most cases, there is a simple for-in loop that can handle the situation:

    for item in array {
        // do something with item
    }

    for (item1, item2) in zip(array, array[1 ..< array.count]) {
        // do something with item1 and item2
    }

    for (index, item1) in array.enumerate() {
        for item2 in array[index + 1 ..< array.count] {
            // do soemthing with item1 and item2
        }
    }

For your last case, you might be justified using a for look, but that is an extremely rare edge case.

Don't litter your code with for loops.

Upvotes: 1

Martin R
Martin R

Reputation: 539795

for var i = 0;  i <= array.count - 1;  i++ {
      //something with array[i]
 }

Here you don't need the element index at all, so you can simply enumerate the array elements:

for elem in array {
    // Do something with elem ...
}

for var i = 0, j = 1;  i <= array.count - 2 && j <= array.count - 1;  i++, j++ {
     //something with array[i] and array[j]
}

To iterate over pairs of adjacent elements, use zip() and dropFirst():

for (x, y) in zip(array, array.dropFirst()) {
    // Do something with x and y ...
    print(x, y)
}

Output:

1 2
2 3
3 4
4 5

For other distances, use dropFirst(n):

for (x, y) in zip(array, array.dropFirst(3)) {
    // Do something with x and y ...
    print(x, y)
}

Output:

1 4
2 5

There are probably many solutions to do

for var i = 0; i <= <array.count - 1; i++ {
    for var j = i + 1; j <= array.count - 1; j++ {
        //something with array[i] and array[j]
    }
}

without a C-style for-loop, here is one:

for (index, x) in array.enumerate() {
    for y in array.dropFirst(index + 1) {
        print(x, y)
    }
}

Upvotes: 5

vikingosegundo
vikingosegundo

Reputation: 52227

Do enumeration

let suits = ["♠︎", "♥︎", "♣︎", "♦︎"]
for (i, suite) in suits.enumerate() {
    // ...
}

or to compare neighbors

import Foundation

let suits = ["♠︎", "♥︎", "♣︎", "♦︎"]
for (i, suite1) in suits.enumerate() {
    let j = i.successor()
    if j < suits.count {
        let suite2 = suits[j]
        // ...
    }
}

or zipping and enumerating

let suits = ["♠︎", "♥︎", "♣︎", "♦︎"]
let combination = zip(suits, suits.dropFirst())
for (i, (s1,s2)) in combination.enumerate() {
    print("\(i): \(s1) \(s2)")
}

result

0: ♠︎ ♥︎
1: ♥︎ ♣︎
2: ♣︎ ♦︎

Upvotes: 2

user4691305
user4691305

Reputation:

for (i, j) in zip(array.dropLast(), array.dropFirst())
{
    // something
}

What you're really doing here is enumerating two parallel sequences. So, create those sequences and use zip to turn them into a single sequence.

Upvotes: 2

Fogmeister
Fogmeister

Reputation: 77641

If you want to do something with subsequent pairs there are many other ways to do it.

Something like this would work...

var previousItem = array.first

for index in 1..<array.count {
    let currentItem = array[index]

    // do something with current and previous items

    previousItem = currentItem
}

Upvotes: 2

Related Questions