Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Split large Array in Array of two elements

I have large list of objects and I need to split them in a group of two elements for UI propouse.

Example:

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

Becomes an array with these four arrays

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

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

like image 341
Douglas Ferreira Avatar asked Jul 02 '15 13:07

Douglas Ferreira


2 Answers

If you want an array of subslices, you can use the split function to generate it using a closure that captures a state variable and increments it as it passes over each element, splitting only on every nth element. As an extension of Sliceable (Swift 2.0 only, would need to be a free function in 1.2):

extension Sliceable {
    func splitEvery(n: Index.Distance) -> [SubSlice] {
        var i: Index.Distance = 0
        return split(self) { _ in ++i % n == 0 }
    }
}

Subslices are very efficient in so much as they usually share internal storage with the original sliceable entity. So no new memory will be allocated for storing the elements - only memory for tracking the subslices' pointers into the original array.

Note, this will work on anything sliceable, like strings:

"Hello, I must be going"
    .characters
    .splitEvery(3)
    .map(String.init)

returns ["He", "lo", " I", "mu", "t ", "e ", "oi", "g"].

If you want to lazily split the array up (i.e. generate a sequence that only serves up subslices on demand) you could write it using anyGenerator:

extension Sliceable {
    func lazilySplitEvery(n: Index.Distance) -> AnySequence<SubSlice> {

        return AnySequence { () -> AnyGenerator<SubSlice> in
            var i: Index = self.startIndex
            return anyGenerator {
                guard i != self.endIndex else { return nil }
                let j = advance(i, n, self.endIndex)
                let r = i..<j
                i = j
                return self[r]
            }
        }
    }
}


for x in [1,2,3,4,5,6,7].lazilySplitEvery(3) {
    print(x)
}
// prints [1, 2, 3]
//        [4, 5, 6]
//        [7]
like image 111
Airspeed Velocity Avatar answered Oct 22 '22 19:10

Airspeed Velocity


The shortest solution (Swift 4), I have seen so far, is from a Gist:

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)])
        }
    }

}
like image 27
sundance Avatar answered Oct 22 '22 20:10

sundance