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)
I personally dislike looping through half the list (mainly because of dividing), so here is how I like to do it:
let array = [1,2,3,4,5];
var i = 0;
while i < array.count {
var a = array[i];
var b : Int? = nil;
if i + 1 < array.count {
b = array[i+1];
}
print("(\(a), \(b))");
i += 2;
}
You loop through the array by incrementing by 2.
If you want to have nil in the element, you need to use optionals.
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"
}
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.
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.
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)
One approach would be to encapsulate the array in a class. The return values for getting pairs of items would be optionals to protect against out-of-range calls.
Example:
class Pairs {
let source = [1, 2, 3, 4, 5] // Or set with init()
var offset = 0
func nextpair() -> (Int?, Int?) {
var first: Int? = nil
var second: Int? = nil
if offset < source.count {
first = source[offset]
offset++
}
if offset < source.count {
second = source[offset]
offset++
}
return (first, second)
}
}