Given I've got an array in Swift such as [1,2,3,4]
, a method pairs()
will transform it in to the array of tuples: [(1,2), (2,3), (3,4)]
.
Here are some more examples of how pairs()
should behave:
pairs([])
should return []
as it has no pairs.pairs([1])
should also return []
, as it has no pairs.pairs([1,2])
should be [(1,2)]
. It has just one pair.I can write code to do this for Array
, but I'd like to have pairs()
available as an extension on Sequence
, so that it returns a Sequence
of the pairs. This would make it useable on any sequence, and compatible with methods such as map
, reduce
, filter
, etc.
How do I go about creating a Sequence
like this? And how do I write the method to transform any Sequence
in this way so that it can be used as flexibly as possible?
We can use zip()
and dropFirst()
if we define an extension
on the Collection
type:
extension Collection {
func pairs() -> AnySequence<(Element, Element)> {
return AnySequence(zip(self, self.dropFirst()))
}
}
Example:
let array = [1, 2, 3, 4]
for p in array.pairs() {
print(p)
}
Output:
(1, 2) (2, 3) (3, 4)
More examples:
print(Array("abc".pairs()))
// [("a", "b"), ("b", "c")]
print([1, 2, 3, 4, 5].pairs().map(+))
// [3, 5, 7, 9]
print([3, 1, 4, 1, 5, 9, 2].pairs().filter(<))
// [(1, 4), (1, 5), (5, 9)]
(Unlike I wrote in the first version of this answer ...) this
approach is not safe when applied to a Sequence
, because it is
not guaranteed that a sequence can be traversed multiple times
non-destructively.
Here is a direct implementation with a custom iterator type which works on sequences as well:
struct PairSequence<S: Sequence>: IteratorProtocol, Sequence {
var it: S.Iterator
var last: S.Element?
init(seq: S) {
it = seq.makeIterator()
last = it.next()
}
mutating func next() -> (S.Element, S.Element)? {
guard let a = last, let b = it.next() else { return nil }
last = b
return (a, b)
}
}
extension Sequence {
func pairs() -> PairSequence<Self> {
return PairSequence(seq: self)
}
}
Example:
print(Array([1, 2, 3, 4].pairs().pairs()))
// [((1, 2), (2, 3)), ((2, 3), (3, 4))]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With