2015-07-02 4 views
6

Ho una lunga lista di oggetti e ho bisogno di dividerli in un gruppo di due elementi per propensione all'interfaccia utente.Dividi array di grandi dimensioni in Array di due elementi

Esempio:

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

diventa un array con queste quattro matrici

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

Ci sono un sacco di modi per dividere un array. Ma, qual è il più efficiente (il meno costoso) se l'array è enorme.

+2

Non ho testato l'efficienza, ma questa contiene una possibile soluzione: http://stackoverflow.com/questions/26691123/how-to-implement-haskells-splitevery-in-swift/26691258#26691258. –

+0

vedere questo http://stackoverflow.com/questions/5731042/split-array-into-two-parts-without-for-loop-in-java – heaphach

+3

@heaphach: non penso che il compilatore Swift accetti il ​​codice sorgente Java :) –

risposta

4

Se siete alla ricerca di efficienza, si potrebbe avere un metodo che genererebbe ogni matrice di 2 elementi pigramente, quindi faresti memorizzare solo 2 elementi in un momento in memoria:

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 

Questo metodo funziona su qualsiasi SequenceType, non solo su Array.

Per Swift 1, senza l'estensione del protocollo, hai:

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

Per 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

Come posso utilizzarlo in Swift 1.2 senza estensione del protocollo? –

+1

lo ha aggiornato proprio lì – oisdk

+0

qualsiasi aggiornamento per Swift 3? – lee

0

È possibile utilizzare oisdk s' impressionante quadro SwiftSequence. C'è la funzione chunk che fa esattamente quello che vuoi:

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

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

Inoltre c'è molto di più funzioni per le sequenze, si dovrebbe verificare out.

Si può avere uno sguardo alla sua attuazione chunkhere (che utilizza generatori)

0

Forse non è la soluzione più efficiente, ma la soluzione più diretta:

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) 

uscita sul mio portatile:

[[0, 1], [2, 3], [4, 5]] 
Program ended with exit code: 0 
0

In alternativa, è possibile utilizzare reduce per questo, ma probabilmente non è il più efficiente:

let res = a.reduce([[Int]]()) { (var acc: [[Int]], current: Int) in 

    if acc.last != nil && acc.last?.count < 2 { 
     var newLast = acc.last 
     newLast?.append(current) 
     acc.removeLast() 

     acc.append(newLast!) 
    } else { 
     acc.append([current]) 
    } 
    return acc 
} 
4

Se si desidera una serie di sottosezioni, è possibile utilizzare la funzione split per generare utilizzando una chiusura che cattura una variabile di stato e lo incrementa quando passa sopra ogni elemento, splitting solo su ogni elemento n-esimo. Come estensione di Sliceable (Swift 2.0 solo, avrebbe bisogno di essere una funzione libera in 1.2):

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

Le sottosezioni sono molto efficienti in tanto come fanno di solito condividono memoria interna con l'entità affettabili originale. Quindi nessuna nuova memoria verrà allocata per la memorizzazione degli elementi - solo memoria per tracciare i puntatori dei subslices nell'array originale.

Nota, questo funzionerà su qualsiasi cosa affettabili, come corde:

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

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

Se si desidera dividere pigramente la matrice fino (cioè generare una sequenza che serve solo fino sottosezioni su richiesta) si potrebbe scrivere utilizzando 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] 
3

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

La soluzione più breve (Swift 4), che ho visto finora, proviene da uno 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)]) 
     } 
    } 

}