Iterate over collection two at a time in Swift

前端 未结 7 894
花落未央
花落未央 2020-11-28 16:00

Say I have an array [1, 2, 3, 4, 5]. How can I iterate two at a time?

Iteration 1: (1, 2)
Iteration 2: (3, 4)
Iteration 3: (5, nil)
相关标签:
7条回答
  • 2020-11-28 16:39

    If the array would have an even number of elements, you would be able to write something like this:

    for i in 0..<arr.count/2 {
        print(arr[2*i...2*i+1])
    }
    

    However that's not always the case. Moreover, nil is not always compatible with the type of elements in array, like the one in your example (nil is not compatible with Int, only with Int?).

    Another solution would be to extend Array and add a pair() method, which returns a tuple (tuples can be heterogenous). You can use pair to walk within all pairs in the array, or, you can extend even more the Array struct and add pairs() that return an array of tuples. Note that since the second element in the tuple is an Optional you'll need to unwrap it before use.

    extension Array {
        func pair(at i: Index) -> (Element, Element?) {
            return (self[i], i < self.count - 1 ? self[i+1] : nil)
        }
    
        func pairs() -> [(Element, Element?)] {
            guard !isEmpty else { return [] }
            var result = [(Element, Element?)]()
            for i in 0...arr.count/2 {
                result.append(self.pair(at: 2*i))
            }
            return result
        }
    }
    
    let arr = [1, 2, 3, 4, 5]
    
    for i in 0...arr.count/2 {
        print(arr.pair(at: 2*i))
    }
    
    for pair in arr.pairs() {
        print(pair)
    }
    

    Update Both above solutions can be simplified by using map instead of manually looping:

    let pairs = (0..<arr.count/2).map { (arr[$0*2], arr[$0*2+1]) }
    print(pairs) // prints [(1, 2), (3, 4)]
    

    or, for the Array extension:

    extension Array {
        func pair(at i: Index) -> (Element, Element?) {
            return (self[i], i < self.count - 1 ? self[i+1] : nil)
        }
    
        func pairs() -> [(Element, Element?)] {
            guard !isEmpty else { return [] }
            return (0..<(arr.count/2 + arr.count%2)).map { pair(at: $0*2) }
        }
    }
    
    let arr = [1, 2, 3, 4, 5]
    print(arr.pairs()) // [(1, Optional(2)), (3, Optional(4)), (5, nil)]
    

    You can extend Collection instead, to have this pair functionality available for all collections:

    extension Collection {
        func pairs() -> [(Element, Element?)] {
            guard !isEmpty else { return [] }
            return (0..<count/2+count%2).map {
                let i1 = index(startIndex, offsetBy: $0*2)
                let i2 = index(after: i1)
                return (self[i1], i2 < endIndex ? self[i2] : nil)
            }
        }
    }
    
    0 讨论(0)
提交回复
热议问题