Split Big Array Into Two Arrays

You can use the for in stride loop to fill two resulting arrays as follow:

extension Array {
    var groupOfTwo:(firstArray:[T],secondArray:[T]) {
        var firstArray:[T] = []
        var secondArray:[T] = []
        for index in stride(from: 0, to: count, by: 2) {
            firstArray.append(self[index])
            if index + 1 < count {
                secondArray.append(self[index+1])
            }
        }
        return (firstArray,secondArray)
    }
}



[0, 1, 2, 3, 4, 5, 6].groupOfTwo.firstArray   // [0, 2, 4, 6]
[0, 1, 2, 3, 4, 5, 6].groupOfTwo.secondArray  // [1, 3, 5]

update: Xcode 7.1.1 • Swift 2.1

extension Array {
    var groupOfTwo:(firstArray:[Element],secondArray:[Element]) {
        var firstArray:[Element] = []
        var secondArray:[Element] = []
        for index in 0.stride(to: count, by: 2) {
            firstArray.append(self[index])
            if index + 1 < count {
                secondArray.append(self[index+1])
            }
        }
        return (firstArray,secondArray)
    }
}

There are various fancy ways to do it with filter but most would probably require two passes rather than one, so you may as well just use a for-loop.

Reserving space up-front could make a big difference in this case since if the source is large it’ll avoid unnecessary re-allocation as the new arrays grow, and the calculation of space needed is in constant time on arrays.

// could make this take a more generic random-access collection source
// if needed, or just make it an array extension instead
func splitAlternating<T>(source: [T]) -> ([T],[T]) {
    var evens: [T] = [], odds: [T] = []

    evens.reserveCapacity(source.count / 2 + 1)
    odds.reserveCapacity(source.count / 2)

    for idx in indices(source) {
        if idx % 2 == 0 {
            evens.append(source[idx])
        }
        else {
            odds.append(source[idx])
        }
    }

    return (evens,odds)
}

let a = [0,1,2,3,4,5,6]
splitAlternating(a)  // ([0, 2, 4, 6], [1, 3, 5])

If performance is truly critical, you could use source.withUnsafeBufferPointer to access the source elements, to avoid the index bounds checking.

If the arrays are really huge, and you aren’t going to use the resulting data except to sample a small number of elements, you could consider using a lazy view instead (though the std lib lazy filter isn’t much use here as it returns sequence not a collection – you’d possibly need to write your own).


A more concise, functional approach would be to use reduce

let a = [0,1,2,3,4,5,6]

let (evens, odds) = a.enumerate().reduce(([Int](),[Int]())) { (cur, next) in
    let even = next.index % 2 == 0
    return (cur.0 + (even ? [next.element] : []),
            cur.1 + (even ? [] : [next.element]))
}

evens // [0,2,4,6]
odds // [1,3,5]

Tags:

Arrays

Swift