Any way to stop a block literal in swift

Swift

Swift Problem Overview


Like below, i want to stop the literal by something like break

var numbers = [1,2,3,4]
numbers.forEach {
    if $0 % 2 == 0 {
        break
    }
}

Swift Solutions


Solution 1 - Swift

forEach is not a loop (it is a block passed to a loop, but not a loop itself), or more accurately, forEach is not part of Swift's Control Flow. Which is why you can't use break or continue.

Just use a for-in loop.


Example :

var numbers = [ 1,2,3,4]

func firstEvenNumber(inArray array: [Int]) -> Int? {
    
    var firstMatch : Int?
    
    for number in numbers {
        if (number % 2) == 0 {
            firstMatch = number
            break
        }
    }
    return firstMatch
}

firstEvenNumber(inArray: numbers) // 2

You can use return inside the forEach closure, but it does not break the loop, it only returns the block in the current pass.

var numbers = [ 1,2,3,4]

func evenNumbers(inArray: [Int]) -> [Int] {
    
    var matches : [Int] = []
    
    numbers.forEach{
        if $0 % 2 == 0 {
            matches.append($0)

            // early return on even numbers
            // does not stop forEach
            // comparable to a continue in a for in loop
            return
        }

        // only reached on uneven numbers
    }
    return matches
}

evenNumbers(numbers) // [2,4]

Solution 2 - Swift

Since you want to stop on the first match, you should use first and not forEach:

numbers.first { (number) -> Bool in
    return number % 2 == 0
}

Do something with the result (the first even number on the list):

if let firstEvenNumber = numbers.first(where: { $0 % 2 == 0 }) {
    print("The first even number on the list is \(firstEvenNumber)")
}

Bonus: create a list of all the even numbers using filter:

let allEvenNumbers = numbers.filter { $0 % 2 == 0 }

Solution 3 - Swift

The document has explained。

    /// - Note: You cannot use the `break` or `continue` statement to exit the
    ///   current call of the `body` closure or skip subsequent calls.
    /// - Note: Using the `return` statement in the `body` closure will only
    ///   exit from the current call to `body`, not any outer scope, and won't
    ///   skip subsequent calls.

But you would like to try this.

extension CollectionType {

    /// Use as
    ///
    ///      let num = [1,2,3,4,5]
    ///      num.forEach{
    ///        if $1 > 3 { $2 = true }
    ///        print($0,$1)
    ///      }
    func forEach(body: ((Self.Generator.Element, Int, inout Bool) -> Void)) {
        var stop = false
        let enumerate = self.enumerate()
        for (index,value) in enumerate {
            if stop { break }
            body(value,index,&stop)
        }
    }
    
    /// Use as
    ///
    ///      let num = [1,2,3,4,5]
    ///      num.forEach{
    ///        if $0 > 3 { $1 = true }
    ///        print($0)
    ///      }
    func forEach(body: ((Self.Generator.Element, inout Bool) -> Void)) {
        var stop = false
        for value in self {
            if stop { break }
            body(value,&stop)
        }
    }
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionWilliam HuView Question on Stackoverflow
Solution 1 - SwiftR MenkeView Answer on Stackoverflow
Solution 2 - SwiftZaporozhchenko OleksandrView Answer on Stackoverflow
Solution 3 - Swiftxu tongView Answer on Stackoverflow