Onichan Onichan - 6 months ago 18
Swift Question

Split Big Array Into Two Arrays

I have a big array of objects and would like to split it into two arrays containing the objects in alternate order.

Example:

[0, 1, 2, 3, 4, 5, 6]


Becomes these two arrays (they should alternate)

[0, 2, 4, 6]
and
[1, 3, 5]


There are a ton of ways to split an array. But, what is the most efficient (least costly) if the array is huge.

Answer

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).