Split large Array in Array of two elements

前端 未结 7 1961
借酒劲吻你
借酒劲吻你 2020-12-20 17:53

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

相关标签:
7条回答
  • 2020-12-20 18:07

    You can use oisdk's awesome SwiftSequence framework. There is the chunk function which does exactly what you want:

    [1, 2, 3, 4, 5].chunk(2)
    
    [[1, 2], [3, 4], [5]]
    

    Also there's a LOT more functions for sequences, you should definitely check it out.

    You can have a look at his implementation of chunk here (It uses generators)

    0 讨论(0)
  • 2020-12-20 18:14

    Maybe not the most efficient solution, but the most direct solution:

    func toPairs(numbers:[Int])->[[Int]]
    {
        var pairs:[[Int]]=[]
        var pair:[Int]=[]
        for var index=0;index<numbers.count;index++ {
            pair.append(numbers[index])
            if pair.count == 2 || index==numbers.count-1 {
                pairs.append(pair)
                pair=[]
            }
        }
        return pairs
    }
    
    var numbers=[0,1,2,3,4,5]
    
    var pairs=toPairs(numbers)
    
    print(pairs)
    

    Output on my laptop:

    [[0, 1], [2, 3], [4, 5]]
    Program ended with exit code: 0
    
    0 讨论(0)
  • 2020-12-20 18:17

    If you're looking for efficiency, you could have a method that would generate each array of 2 elements lazily, so you'd only store 2 elements at a time in memory:

    public struct ChunkGen<G : GeneratorType> : GeneratorType {
    
      private var g: G
      private let n: Int
      private var c: [G.Element]
    
      public mutating func next() -> [G.Element]? {
        var i = n
        return g.next().map {
          c = [$0]
          while --i > 0, let next = g.next() { c.append(next) }
          return c
        }
      }
    
      private init(g: G, n: Int) {
        self.g = g
        self.n = n
        self.c = []
        self.c.reserveCapacity(n)
      }
    }
    
    public struct ChunkSeq<S : SequenceType> : SequenceType {
    
      private let seq: S
      private let n: Int
    
      public func generate() -> ChunkGen<S.Generator> {
        return ChunkGen(g: seq.generate(), n: n)
      }
    }
    
    public extension SequenceType {
      func chunk(n: Int) -> ChunkSeq<Self> {
        return ChunkSeq(seq: self, n: n)
      }
    }
    
    var g = [1, 2, 3, 4, 5].chunk(2).generate()
    
    g.next() // [1, 2]
    g.next() // [3, 4]
    g.next() // [5]
    g.next() // nil
    

    This method works on any SequenceType, not just Arrays.

    For Swift 1, without the protocol extension, you've got:

    public struct ChunkGen<T> : GeneratorType {
    
      private var (st, en): (Int, Int)
      private let n: Int
      private let c: [T]
    
      public mutating func next() -> ArraySlice<T>? {
        (st, en) = (en, en + n)
        return st < c.endIndex ? c[st..<min(en, c.endIndex)] : nil
      }
    
      private init(c: [T], n: Int) {
        self.c = c
        self.n = n
        self.st = 0 - n
        self.en = 0
      }
    }
    
    public struct ChunkSeq<T> : SequenceType {
    
      private let c: [T]
      private let n: Int
    
      public func generate() -> ChunkGen<T> {
        return ChunkGen(c: c, n: n)
      }
    }
    
    func chunk<T>(ar: [T], #n: Int) -> ChunkSeq<T> {
      return ChunkSeq(c: ar, n: n)
    }
    

    For Swift 3:

    public struct ChunkIterator<I: IteratorProtocol> : IteratorProtocol {
    
      fileprivate var i: I
      fileprivate let n: Int
    
      public mutating func next() -> [I.Element]? {
        guard let head = i.next() else { return nil }
        var build = [head]
        build.reserveCapacity(n)
        for _ in (1..<n) {
          guard let x = i.next() else { break }
          build.append(x)
        }
        return build
      }
    
    }
    
    public struct ChunkSeq<S: Sequence> : Sequence {
    
      fileprivate let seq: S
      fileprivate let n: Int
    
      public func makeIterator() -> ChunkIterator<S.Iterator> {
        return ChunkIterator(i: seq.makeIterator(), n: n)
      }
    }
    
    public extension Sequence {
      func chunk(_ n: Int) -> ChunkSeq<Self> {
        return ChunkSeq(seq: self, n: n)
      }
    }
    
    var g = [1, 2, 3, 4, 5].chunk(2).makeIterator()
    
    g.next() // [1, 2]
    g.next() // [3, 4]
    g.next() // [5]
    g.next() // nil
    
    0 讨论(0)
  • 2020-12-20 18:17

    Swift 2 Gist

    let arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
    
    extension Array {
        func splitBy(subSize: Int) -> [[Element]] {
            return 0.stride(to: self.count, by: subSize).map { startIndex in
                let endIndex = startIndex.advancedBy(subSize, limit: self.count)
                return Array(self[startIndex ..< endIndex])
            }
        }
    }
    
    let chunks = arr.splitBy(5)
    
    print(chunks) // [[1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12]]
    
    0 讨论(0)
  • 2020-12-20 18:21

    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]
    
    0 讨论(0)
  • 2020-12-20 18:25

    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)])
            }
        }
    
    }
    
    0 讨论(0)
提交回复
热议问题