Swift: how to execute a task n-1 times where n is the length of the array?

I ended up with the following:

for x in 1...(myArray.count - 1) { task() }

      

which is very ugly. Is there a better way?

+3


source to share


3 answers


You have to be a little careful, as if the array is empty, this will work:

let a: [Int] = []
let range = 0..<a.count-1
// fatal error: Can't form Range with end < start

      

Strides doesn't have this problem (as Strideable

things should be Comparable

) so you can:

for _ in stride(from: 0, to: a.count - 1, by: 1) {
    // will only execute if a.count > 2
    print("blah")
}

      

Alternatively, if you are guarding it, you can use dropFirst

:



for _ in (a.isEmpty ? [] : dropFirst(a))  {
    print("blah")
}

      

I would strongly advise against trying to make this look neater by creating a pseudo-for-loop that runs one less than the number of samples. This is the reason why no forEach

or repeat

. These types of loops seem good at first, but there are many bugs that arise from them (for example, return

or continue

do not work as you might expect, and it is also in its usual bad practice to use a higher order function to do external mutation, whereas regular cycle for

represents the assumption that mutation / side effects are likely).

The most convenient extension type solution would probably be an extension Array

to make a safe fall:

extension Array {
    // version of dropFirst that returns empty array for both
    // empty and single-element array
    func safeDropFirst() -> ArraySlice<T> {
        return self.isEmpty ? [] : dropFirst(self)
    }
}

for _ in myArray.safeDropFirst() {
    doThing()
}

      

+5


source


Not much better, but:

for _ in 1..<myArray.count { task() }

      

But it will work if myArray

empty (thanks, Airspeed ).



If you need a lot for some reason, you can provide your own "loop abstraction" and take care of this problem:

func repeatArray<T>(arr: [T], @noescape f: () -> Void) {
    if !arr.isEmpty {
        for _ in 1..<arr.count {
            f()
        }
    }
}

repeatArray(myArray) {
    task()
}

      

+4


source


Here's the way with Swift 2:

for x in myArray where x != myArray.first { task() }

      

0


source







All Articles