Iterate over collection two at a time in Swift

Extension to split the array.

extension Array {
   func chunked(into size: Int) -> [[Element]] {
      return stride(from: 0, to: count, by: size).map {
      Array(self[$0 ..< Swift.min($0 + size, count)]) }
   }
}

let result = [1...10].chunked(into: 2)

This is not identically what was asked, but I use an extension on Sequence that generates an array of arrays chunking the original sequence by any desired size:

extension Sequence {
    func clump(by clumpsize:Int) -> [[Element]] {
        let slices : [[Element]] = self.reduce(into:[]) {
            memo, cur in
            if memo.count == 0 {
                return memo.append([cur])
            }
            if memo.last!.count < clumpsize {
                memo.append(memo.removeLast() + [cur])
            } else {
                memo.append([cur])
            }
        }
        return slices
    }
}

So [1, 2, 3, 4, 5].clump(by:2) yields [[1, 2], [3, 4], [5]] and now you can iterate through that if you like.


You can use sequence() and the iterator's next() method to iterate over pairs of consecutive elements. This works for arbitrary sequences, not only arrays:

let a = "ABCDE"

for pair in sequence(state: a.makeIterator(), next: { it in
    it.next().map { ($0, it.next()) }
}) {
    print(pair)
}

Output:

("A", Optional("B"))
("C", Optional("D"))
("E", nil)

The “outer” it.next() yields the elements at even positions, or nil (in which case it.next().map { } evaluates to nil as well, and the sequence terminates). The “inner” it.next() yields the elements at odd positions or nil.

As an extension method for arbitrary sequences:

extension Sequence {
    func pairs() -> AnyIterator<(Element, Element?)> {
        return AnyIterator(sequence(state: makeIterator(), next: { it in
            it.next().map { ($0, it.next()) }
        }))
    }
}

Example:

let seq = (1...).prefix(5)
for pair in seq.pairs() { print(pair) }

Note that the pairs are generated lazily, no intermediate array is created. If you want an array with all pairs then

let pairs = Array([1, 2, 3, 4, 5].pairs())
print(pairs) // [(1, Optional(2)), (3, Optional(4)), (5, nil)]

does the job.


You can use a progression loop called stride(to:, by:) to iterate over your elements every n elements:

let array = Array(1...5)

let pairs = stride(from: 0, to: array.endIndex, by: 2).map {
    (array[$0], $0 < array.index(before: array.endIndex) ? array[$0.advanced(by: 1)] : nil)
}   // [(.0 1, {some 2}), (.0 3, {some 4}), (.0 5, nil)]

print(pairs)  // "[(1, Optional(2)), (3, Optional(4)), (5, nil)]\n"

To iterate your collection subsequences instead of tuples:

extension Collection {
    func unfoldSubSequences(limitedTo maxLength: Int) -> UnfoldSequence<SubSequence,Index> {
        sequence(state: startIndex) { start in
            guard start < self.endIndex else { return nil }
            let end = self.index(start, offsetBy: maxLength, limitedBy: self.endIndex) ?? self.endIndex
            defer { start = end }
            return self[start..<end]
        }
    }
}

let array = Array(1...5)
for subsequence in array.unfoldSubSequences(limitedTo: 2) {
    print(subsequence)  // [1, 2] [3, 4] [5]
}

This would work on any kind of collection:

let string = "12345"
for substring in string.unfoldSubSequences(limitedTo: 2) {
    print(substring)  // "12" "34" "5"
}

Tags:

Swift