Spilt Array into pairs

I am trying to split array into pairs. I am able to split into consecutive pair but I want to split into pair which includes previous value as mentioned in Results

Logic to split in consecutive pair, which I tried.

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

Array:

["1", "2", "3", "4", "5", "6", "7", "8", "9"]

Results:

[["1", "2"], ["2", "3"], ["3", "4"], ["4", "5"], ["6", "7"], ["7", "8"], ["8", "9"]]

Solution 1:

Not a direct answer to the question but the elements of the sequence should be computed lazily. You should use Swift UnfoldSequence type as follow:

extension Collection {
    var unfoldedNeighbors: UnfoldSequence<SubSequence,Index> {
        sequence(state: startIndex) { start in
            guard start < endIndex else { return nil }
            guard let end = index(start, offsetBy: 2, limitedBy: endIndex) else {
                return nil
            }
            defer { formIndex(after: &start) }
            return self[start..<end]
        }
    }
    var neighborsSubsequences: [SubSequence] {
        .init(unfoldedNeighbors)
    }
    var neighborsArrays: [[Element]] {
        unfoldedNeighbors.map([Element].init)
    }
}

Usage:

let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9]
for neighbors in numbers.unfoldedNeighbors {
    print(neighbors)
}

If you need control the number of elements of each subsequence and also if it includes the tail or not:

extension Collection {
    func unfoldedNeighbors(limitedTo length: Int, includesTail: Bool = false) -> UnfoldSequence<SubSequence,Index> {
        sequence(state: startIndex) { start in
            guard start < endIndex else { return nil }
            guard let end = index(start, offsetBy: length, limitedBy: endIndex) else {
                if includesTail {
                    defer { formIndex(&start, offsetBy: length-1, limitedBy: endIndex) }
                    return self[start...]
                }
                return nil
            }
            defer { formIndex(&start, offsetBy: length-1, limitedBy: endIndex) }
            return self[start..<end]
        }
    }
    func neighborsSequences(limitedTo length: Int, includesTail: Bool = false) -> [SubSequence] {
        .init(unfoldedNeighbors(limitedTo: length, includesTail: includesTail))
    }
    func neighborsArrays(limitedTo length: Int, includesTail: Bool = false) -> [[Element]] {
        unfoldedNeighbors(limitedTo: length, includesTail: includesTail).map([Element].init)
    }
}

Usage:

let numbers = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
for neighbors in numbers.unfoldedNeighbors(limitedTo: 3, includesTail: true) {
    print(neighbors)
}

This will print:

[1, 2, 3]
[3, 4, 5]
[5, 6, 7]
[7, 8, 9]
[9, 10]


let neighborsSequences = a.neighborsSequences(limitedTo: 3, includesTail: true)  // [[1, 2, 3], [3, 4, 5], [5, 6, 7], [7, 8, 9], [9, 10]] of type [Array<Int>.SubSequence]
let neighborsArrays = a.neighborsArrays(limitedTo: 3, includesTail: true)        // [[1, 2, 3], [3, 4, 5], [5, 6, 7], [7, 8, 9], [9, 10]] of type [[Int]]

Solution 2:

How about this:

let a = [ 1, 2, 3, 4, 5, 6, 7, 8, 9]

let pairs = Array(zip(a, a.dropFirst())).map {[$0.0, $0.1] }

print(pairs)

That outputs

[[1, 2], [2, 3], [3, 4], [4, 5], [5, 6], [6, 7], [7, 8], [8, 9]]

Edit:

If you want arbitrary chunk-size, you could write the extension like this:

extension Array {
    func chunks(_ chunkSize: Int, includePartialChunk: Bool = true) -> [[Element]] {
        var indexes = Array<Int>(stride(from: 0, to: count, by: chunkSize - 1))
        if includePartialChunk,
           let last = indexes.last,
           last < count - 1 {
            indexes.append(count-1)
        }
        return zip(indexes, indexes.dropFirst()).map {Array(self[$0.0...$0.1])}
    }
}

Use the parameter includePartialChunk to tell the function if you want to include a "partial chunk" at the end when the array size is not an even multiple of the chunk-size. If true (The default) it returns a last chunk that is smaller than chunkSize but goes to the end of the array. If false, it only returns full-sized chunks, but will skip array elements at the end that don't fit into a full-sized chunk.

(I'll have to study Leo's UnfoldSequence version and see if I can adapt my code to that.)