9

I understand that in Swift 3 there have been some changes from typical C Style for-loops. I've been working around it, but it seems like I'm writing more code than before in many cases. Maybe someone can steer me in the right direction because this is what I want:

let names : [String] = ["Jim", "Jenny", "Earl"]
for var i = 0; i < names.count - 1; i+=1 {
    NSLog("%@ loves %@", names[i], names[i+1])
}

Pretty simple stuff. I like to be able to get the index I'm on, and I like the for-loop to not run if names.count == 0. All in one go.

But it seems like my options in Swift 3 aren't allowing me this. I would have to do something like:

let names : [String] = ["Jim", "Jenny", "Earl"]
if names.count > 0 {
    for i in 0...(names.count - 1) {
        NSLog("%@ loves %@", names[i], names[i+1])
    }
}

The if statement at the start is needed because my program will crash in the situation where it reads: for i in 0...0 { }

I also like the idea of being able to just iterate through everything without explicitly setting the index:

// Pseudocode
for name in names.exceptLastOne {
    NSLog("%@ loves %@", name, name.plus(1))
}

I feel like there is some sort of syntax that mixes all my wants, but I haven't come across it yet. Does anyone know of a way? Or at least a way to make my code more compact?

UPDATE: Someone suggested that this question has already been asked, citing a SO post where the solution was to use something to the degree of:

for (index, name) in names.enumerated {}

The problem with this when compared to Hamish's answer is that I only am given the index of the current name. That doesn't allow me to get the value at index without needing to do something like:

names[index + 1]

That's just one extra variable to keep track of. I prefer Hamish's which is:

for i in names.indices.dropLast() {
    print("\(names[i]) loves \(names[i + 1])")
}

Short, simple, and only have to keep track of names and i, rather than names, index, and name.

9
  • Possible duplicate of swift for loop: for index, element in array? Commented Dec 4, 2016 at 23:39
  • 2
    for name in names.dropLast() { Commented Dec 4, 2016 at 23:41
  • 3
    @LeoDabus But the OP needs to access two different objects from the array during each iteration. Commented Dec 4, 2016 at 23:45
  • 1
    for (index, name) in names.dropLast().enumerated() { Commented Dec 4, 2016 at 23:47
  • @LeoDabus Post an answer. Commented Dec 4, 2016 at 23:49

1 Answer 1

14

One option would be to use dropLast() on the array's indices, allowing you to iterate over a CountableRange of all but the last index of the array.

let names = ["Jim", "Jenny", "Earl"]

for i in names.indices.dropLast() {
    print("\(names[i]) loves \(names[i + 1])")
}

If the array has less than two elements, the loop won't be entered.

Another option would be to zip the array with the array where the first element has been dropped, allowing you to iterate through the pairs of elements with their successor elements:

for (nameA, nameB) in zip(names, names.dropFirst()) {
    print("\(nameA) loves \(nameB)")
}

This takes advantage of the fact that zip truncates the longer of the two sequences if they aren't of equal length. Therefore if the array has less than two elements, again, the loop won't be entered.

Sign up to request clarification or add additional context in comments.

1 Comment

Wow, I didn't know about zip! That's awesome! Also didn't know about indicies.dropLast(). Will definately make use of that as I like having a reference to the index.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.