Dividi l'array in blocchi


516

Diciamo che ho un array Javascript simile al seguente:

["Element 1","Element 2","Element 3",...]; // with close to a hundred elements.

Quale approccio sarebbe appropriato per dividere (dividere) l'array in molti array più piccoli con, diciamo, 10 elementi al massimo?



Possibile duplicato della divisione di un array JS in N array
TJ

24
Per gli utenti lodash , stai cercando _.chunk .
Ulysse BN,

2
Ho appena provato const chunk = (arr, n) => arr.length ? [arr.slice(0, n), ...chunk(arr.slice(n), n)] : []che è bello e breve ma sembra impiegare circa 256 × fino a quando la risposta di @ AymKdn per 1.000 elementi e 1.058 × per 10.000 elementi!
Toph,

se hai bisogno anche delle dimensioni minime dell'ultimo pezzo, ecco le opzioni: stackoverflow.com/questions/57908133/…
Ahmet Cetin,

Risposte:


643

Il metodo array.slice può estrarre una sezione dall'inizio, al centro o alla fine di una matrice per qualsiasi scopo sia necessario, senza cambiare la matrice originale.

var i,j,temparray,chunk = 10;
for (i=0,j=array.length; i<j; i+=chunk) {
    temparray = array.slice(i,i+chunk);
    // do whatever
}

19
Ricorda se questa è una funzione util per affermare di non chunkessere 0. (loop infinito)
Steven Lu

19
No, l'ultimo pezzo dovrebbe essere più piccolo degli altri.
Blazemonger,

7
@Blazemonger, davvero! La prossima volta lo proverò da solo prima di saltare alle conclusioni. Ho assunto (erroneamente) che passare un input in array.slice che superasse i limiti dell'array sarebbe un problema, ma funziona perfettamente!
rysqui,

55
Per one-liner (catene di amanti) : const array_chunks = (array, chunk_size) => Array(Math.ceil(array.length / chunk_size)).fill().map((_, index) => index * chunk_size).map(begin => array.slice(begin, begin + chunk_size));.
Константин Ван,

8
Perché hai bisogno di j? Per prima cosa ho pensato che fosse un'ottimizzazione, ma in realtà è più lenta di for (i = 0; i <array.length; i ++) {}
Alex

149

Modificato da una risposta di dbaseman: https://stackoverflow.com/a/10456344/711085

Object.defineProperty(Array.prototype, 'chunk_inefficient', {
  value: function(chunkSize) {
    var array = this;
    return [].concat.apply([],
      array.map(function(elem, i) {
        return i % chunkSize ? [] : [array.slice(i, i + chunkSize)];
      })
    );
  }
});

console.log(
  [1, 2, 3, 4, 5, 6, 7].chunk_inefficient(3)
)
// [[1, 2, 3], [4, 5, 6], [7]]


addendum minore :

Devo sottolineare che quanto sopra è una soluzione non così elegante (nella mia mente) da usare Array.map. Fondamentalmente fa quanto segue, dove ~ è concatenazione:

[[1,2,3]]~[]~[]~[] ~ [[4,5,6]]~[]~[]~[] ~ [[7]]

Ha lo stesso tempo di esecuzione asintotico del metodo seguente, ma forse un fattore costante peggiore a causa della creazione di elenchi vuoti. Si potrebbe riscrivere questo come segue (principalmente lo stesso del metodo di Blazemonger, motivo per cui inizialmente non ho inviato questa risposta):

Metodo più efficiente:

// refresh page if experimenting and you already defined Array.prototype.chunk

Object.defineProperty(Array.prototype, 'chunk', {
  value: function(chunkSize) {
    var R = [];
    for (var i = 0; i < this.length; i += chunkSize)
      R.push(this.slice(i, i + chunkSize));
    return R;
  }
});

console.log(
  [1, 2, 3, 4, 5, 6, 7].chunk(3)
)


Il mio modo preferito al giorno d'oggi è il precedente, o uno dei seguenti:

Array.range = function(n) {
  // Array.range(5) --> [0,1,2,3,4]
  return Array.apply(null,Array(n)).map((x,i) => i)
};

Object.defineProperty(Array.prototype, 'chunk', {
  value: function(n) {

    // ACTUAL CODE FOR CHUNKING ARRAY:
    return Array.range(Math.ceil(this.length/n)).map((x,i) => this.slice(i*n,i*n+n));

  }
});

demo:

> JSON.stringify( Array.range(10).chunk(3) );
[[1,2,3],[4,5,6],[7,8,9],[10]]

O se non vuoi una funzione Array.range, in realtà è solo una linea (esclusa la lanugine):

var ceil = Math.ceil;

Object.defineProperty(Array.prototype, 'chunk', {value: function(n) {
    return Array(ceil(this.length/n)).fill().map((_,i) => this.slice(i*n,i*n+n));
}});

o

Object.defineProperty(Array.prototype, 'chunk', {value: function(n) {
    return Array.from(Array(ceil(this.length/n)), (_,i)=>this.slice(i*n,i*n+n));
}});

41
Eh, eviterei di fare confusione con il prototipo poiché la sensazione di frescura che provi chiamando la chunkfunzione sull'array non supera in realtà la complessità aggiuntiva che stai aggiungendo e i bug sottili che possono causare problemi con i prototipi incorporati.
Gordon Gustafson il

12
Non sta scherzando con loro, li sta estendendo per gli array. Capisco di non toccare mai Object.prototype perché questo farebbe il bubble su tutti gli oggetti (tutto) ma per questa funzione specifica dell'array non vedo alcun problema.
rlemon,

Abbastanza sicuro che dovrebbe essere array.map(function(i)non array.map(function(elem,i)però
Nigel Angelo

3
Basato sulla tabella di compatibilità sul sito di sviluppo mozilla, Array.map per per IE9 +. Stai attento.
Maikel D,

Fai attenzione a passare il galleggiamento - 7.5 tipi di numeri - porta a blocchi imprevedibili
Gal Bracha,

103

Ecco una versione ES6 usando ridurre

var perChunk = 2 // items per chunk    

var inputArray = ['a','b','c','d','e']

var result = inputArray.reduce((resultArray, item, index) => { 
  const chunkIndex = Math.floor(index/perChunk)

  if(!resultArray[chunkIndex]) {
    resultArray[chunkIndex] = [] // start a new chunk
  }

  resultArray[chunkIndex].push(item)

  return resultArray
}, [])

console.log(result); // result: [['a','b'], ['c','d'], ['e']]

E sei pronto per concatenare ulteriormente la mappa / ridurre le trasformazioni. L'array di input viene lasciato intatto


Se preferisci una versione più breve ma meno leggibile, puoi cospargerne alcune concatnel mix per lo stesso risultato finale:

inputArray.reduce((all,one,i) => {
   const ch = Math.floor(i/perChunk); 
   all[ch] = [].concat((all[ch]||[]),one); 
   return all
}, [])

Questa sembra la soluzione più condensata. Cosa sta ottenendo chunkIndex = Math.floor (index / perChunk)? È la media?
me-me,

5/2 = 2.5e Math.floor(2.5) = 2quindi l'oggetto con l'indice 5 verrà inserito nel bucket 2
Andrei R

Grazie Andrei R. Ah, quindi passa da 0 a 2. Quindi, come si chiama in termini matematici? Immagino che il mio punto sia che non avrei mai pensato di dividere index / 2 ogni indice per ottenere l'indice di ogni sezione. Quindi sto cercando di avvolgerci la testa perché mi piace davvero, ma non lo capisco completamente in termini matematici. Di solito lo faccio per ottenere medie di un numero totale, ma questo sembra diverso.
me-me,

Questa soluzione è inefficiente rispetto ad altre soluzioni perché è necessario scorrere su ogni elemento.
JP de la Torre,

hai ragione @JPdelaTorre, probabilmente non è efficiente come le soluzioni che tagliano le matrici, ma qui stai dividendo i capelli. la maggior parte delle risposte elencate sarebbe inefficace secondo tale definizione.
Andrei R,

102

Cerca di evitare confusioni con prototipi nativi, incluso Array.prototype, se non sai chi consumerà il tuo codice (terze parti, colleghi, te stesso in un secondo momento, ecc.).

Esistono modi per estendere in modo sicuro i prototipi (ma non in tutti i browser) e esistono modi per consumare in modo sicuro oggetti creati da prototipi estesi, ma una regola empirica migliore è seguire il Principio della Minima Sorpresa ed evitare del tutto queste pratiche.

Se hai un po 'di tempo, guarda il discorso di Andrew Dupont sul JSConf 2011, "Tutto è permesso: estendere gli integrati" , per una buona discussione su questo argomento.

Ma torniamo alla domanda, mentre le soluzioni sopra funzioneranno, sono eccessivamente complesse e richiedono spese generali di calcolo non necessarie. Ecco la mia soluzione:

function chunk (arr, len) {

  var chunks = [],
      i = 0,
      n = arr.length;

  while (i < n) {
    chunks.push(arr.slice(i, i += len));
  }

  return chunks;
}

// Optionally, you can do the following to avoid cluttering the global namespace:
Array.chunk = chunk;

25
"evitare di confondere con i prototipi nativi" i nuovi sviluppatori di js dovrebbero ottenere un tatuaggio temporaneo, se non permanente, di questa frase.
Jacob Dalton,

2
Uso javascript da anni e non trascorro quasi mai a preoccuparmi del prototipo, con la maggior parte delle funzioni di chiamata, senza mai modificare come vedi alcune persone.
1984,

2
il miglior suggerimento ai miei occhi, il più semplice da capire e da implementare, grazie mille!
Olga Farber,

1
@JacobDalton Penso che sia colpa di tutte le università. La gente pensa che OOP debba essere usato ovunque. Quindi hanno paura di "solo creare una funzione". Vogliono essere sicuri di metterlo dentro qualcosa. Anche se non è affatto appropriato. Se non c'è una notazione a punti, non c'è "architettura".
Gherman,

51

Ho testato le diverse risposte in jsperf.com. Il risultato è disponibile qui: https://web.archive.org/web/20150909134228/https://jsperf.com/chunk-mtds

E la funzione più veloce (e che funziona da IE8) è questa:

function chunk(arr, chunkSize) {
  var R = [];
  for (var i=0,len=arr.length; i<len; i+=chunkSize)
    R.push(arr.slice(i,i+chunkSize));
  return R;
}

1
Grazie @AymKdn per aver reso questo punto di riferimento: è stato così utile! Stavo usando l' approccio splice e si è bloccato il mio browser Chrome v70 con una dimensione di chunk di 884432. Con l'approccio "slice" suggerito in atto, il mio codice non blocca più il processo di "rendering" del browser. :)
Benny Neugebauer,

Ecco una versione dattiloscritta di questo: function chunk<T>(array: T[], chunkSize: number): T[][] { const R = []; for (let i = 0, len = array.length; i < len; i += chunkSize) R.push(array.slice(i, i + chunkSize)); return R; }
MacKinley Smith

34

Preferirei usare il metodo splice :

var chunks = function(array, size) {
  var results = [];
  while (array.length) {
    results.push(array.splice(0, size));
  }
  return results;
};

16
È necessario fare attenzione con questa soluzione di giunzione poiché modifica l'array originale, quindi assicurarsi di documentare chiaramente gli effetti collaterali.
bdrx,

3
Quindi usa invece slice
mplungjan il

@mplungjan Il risultato sarebbe lo stesso array ripetutamente quando si utilizza slice. Quindi non è davvero un rimpiazzo senza ulteriori modifiche.
nietonfir il

L'unica cosa che aggiungerei a questa risposta è un clone dell'array originale. Lo farei con l'operatore di diffusione ES6. var clone = [...array]quindi esegui il controllo e la giunzione della lunghezza su quell'array clonato.
MauricioLeal,

1
Oppure, se non puoi utilizzare le funzionalità ES6, puoi semplicemente array = array.slice()creare anche una copia superficiale.
3limin4t0r,

34

One-liner in ECMA 6

const [list,chuckSize] = [[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15], 6]

new Array(Math.ceil(list.length / chuckSize)).fill().map(_ => list.splice(0,chuckSize))

9
modifica l' listarray originale
Jacka, il

6
Facile soluzione usando .slice () .. .map((_,i) => list.slice(i*chuckSize,i*chuckSize+chuckSize))
James Robey

2
Su JSPerf questo è drasticamente più performante di molte altre risposte.
Micah Henning,

30

Vecchia domanda: nuova risposta! In realtà stavo lavorando con una risposta a questa domanda e un amico mi ha migliorato! Quindi eccolo qui:

Array.prototype.chunk = function ( n ) {
    if ( !this.length ) {
        return [];
    }
    return [ this.slice( 0, n ) ].concat( this.slice(n).chunk(n) );
};

[1,2,3,4,5,6,7,8,9,0].chunk(3);
> [[1,2,3],[4,5,6],[7,8,9],[0]]

14
a proposito, le prestazioni di questo metodo sono O (N ^ 2), quindi non dovrebbero essere usate in sezioni di codice critiche per le prestazioni, o con array lunghi (in particolare, quando l'array .lengthè molto più grande della dimensione del blocco n). Se questo fosse un linguaggio pigro (a differenza di JavaScript), questo algoritmo non risentirebbe del tempo O (N ^ 2). Detto questo, l'implementazione ricorsiva è elegante. Probabilmente puoi modificarlo per migliorare le prestazioni definendo prima una funzione di supporto che ricorre array,position, quindi inviando : Array.prototype.chunkrestituisce [la tua funzione di supporto] (...)
ninjagecko,

grazie sensai per avermi benedetto con il tuo spirito che dovevo aver cantato stanotte
morso l'

1
o ...var chunk = (arr, n) => { if ( !arr.length ) return []; return [ arr.slice( 0, n ) ].concat( chunk(arr.slice(n), n) ) }
Ed Williams,

28

Oggi puoi usare la funzione chunk di lodash per dividere l'array in array più piccoli https://lodash.com/docs#chunk Non c'è più bisogno di giocherellare con i loop!


3
Sento che ci dovrebbero essere dichiarazioni di non responsabilità su domande javascript SO: hai provato lodash? Praticamente la prima cosa che includo nel nodo o nel browser.
Jacob Dalton,

20

Ci sono state molte risposte ma questo è quello che uso:

const chunk = (arr, size) =>
  arr
    .reduce((acc, _, i) =>
      (i % size)
        ? acc
        : [...acc, arr.slice(i, i + size)]
    , [])

// USAGE
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
chunk(numbers, 3)

// [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]]

Innanzitutto, controlla la presenza di un residuo quando dividi l'indice per la dimensione del blocco.

Se è presente un resto, restituire semplicemente l'array dell'accumulatore.

Se non vi è resto, l'indice è divisibile per la dimensione del blocco, quindi prendi una porzione dall'array originale (a partire dall'indice corrente) e aggiungila all'array dell'accumulatore.

Quindi, l'array di accumulatori restituiti per ciascuna iterazione di riduci è simile al seguente:

// 0: [[1, 2, 3]]
// 1: [[1, 2, 3]]
// 2: [[1, 2, 3]]
// 3: [[1, 2, 3], [4, 5, 6]]
// 4: [[1, 2, 3], [4, 5, 6]]
// 5: [[1, 2, 3], [4, 5, 6]]
// 6: [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
// 7: [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
// 8: [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
// 9: [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]]

Bella soluzione e bella rappresentazione visiva delle iterazioni. Ho finito con una soluzione molto simile che ho postato come una risposta: stackoverflow.com/a/60779547
mt knn

15

Utilizzando generatori

function* chunks(arr, n) {
 for(let i = 0; i < arr.length; i += n) {
     yield(arr.slice(i, i+n));
     }
}
let someArray = [0,1,2,3,4,5,6,7,8,9]
console.log([...chunks(someArray, 2)]) // [[0,1],[2,3],[4,5],[6,7],[8,9]]


3
Sono stato sorpreso da tutte le risposte a questa domanda che stavano quasi usando i generatori o usandoli in modi più complicati. Bella brevità e prestazioni con questa soluzione.
Keith

14

Ok, iniziamo con uno abbastanza stretto:

function chunk(arr, n) {
    return arr.slice(0,(arr.length+n-1)/n|0).
           map(function(c,i) { return arr.slice(n*i,n*i+n); });
}

Che viene utilizzato in questo modo:

chunk([1,2,3,4,5,6,7], 2);

Quindi abbiamo questa funzione di riduttore stretto:

function chunker(p, c, i) {
    (p[i/this|0] = p[i/this|0] || []).push(c);
    return p;
}

Che viene utilizzato in questo modo:

[1,2,3,4,5,6,7].reduce(chunker.bind(3),[]);

Poiché un gattino muore quando ci leghiamo thisa un numero, possiamo invece eseguire il curry manuale in questo modo:

// Fluent alternative API without prototype hacks.
function chunker(n) {
   return function(p, c, i) {
       (p[i/n|0] = p[i/n|0] || []).push(c);
       return p;
   };
}

Che viene utilizzato in questo modo:

[1,2,3,4,5,6,7].reduce(chunker(3),[]);

Quindi la funzione ancora piuttosto stretta che fa tutto in una volta sola:

function chunk(arr, n) {
    return arr.reduce(function(p, cur, i) {
        (p[i/n|0] = p[i/n|0] || []).push(cur);
        return p;
    },[]);
}

chunk([1,2,3,4,5,6,7], 3);

Non funziona in iE8.
Nadeemmnn Mohd,

4
HA! amo il commento del gattino. scusate per nessun ulteriore input costruttivo :)
29er

Lo farei (p[i/n|0] || (p[i/n|0] = [])), quindi non assegnare un valore, se non necessario ...
yckart

12

Ho mirato a creare una semplice soluzione non mutante in ES6 puro. Le peculiarità in javascript rendono necessario riempire l'array vuoto prima della mappatura :-(

function chunk(a, l) { 
    return new Array(Math.ceil(a.length / l)).fill(0)
        .map((_, n) => a.slice(n*l, n*l + l)); 
}

Questa versione con ricorsione sembra più semplice e più avvincente:

function chunk(a, l) { 
    if (a.length == 0) return []; 
    else return [a.slice(0, l)].concat(chunk(a.slice(l), l)); 
}

Le funzioni di array ridicolmente deboli di ES6 sono ottimi puzzle :-)


1
Ho anche scritto il mio in questo modo. Funziona ancora se rimuovi il 0da fill, il che rende l' fillaspetto un po 'più sensato, imho.
Coert Grobbelaar,

12

Penso che questa sia una bella soluzione ricorsiva con sintassi ES6:

const chunk = function(array, size) {
  if (!array.length) {
    return [];
  }
  const head = array.slice(0, size);
  const tail = array.slice(size);

  return [head, ...chunk(tail, size)];
};

console.log(chunk([1,2,3], 2));


9

Creato un pacchetto npm per questo https://www.npmjs.com/package/array.chunk

var result = [];

for (var i = 0; i < arr.length; i += size) {
  result.push(arr.slice(i, size + i));
}
return result;

Quando si utilizza un TypedArray

var result = [];

for (var i = 0; i < arr.length; i += size) {
  result.push(arr.subarray(i, size + i));
}
return result;

@ A1rPun Mio male, non ho aggiunto commenti lì. Sì, non esiste un slicemetodo per TypedArray, possiamo usare subarrayinvece developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
zhiyelee

8

Se si utilizza la versione EcmaScript> = 5.1, è possibile implementare una versione funzionale chunk()dell'uso di array.reduce () con complessità O (N):

function chunk(chunkSize, array) {
    return array.reduce(function(previous, current) {
        var chunk;
        if (previous.length === 0 || 
                previous[previous.length -1].length === chunkSize) {
            chunk = [];   // 1
            previous.push(chunk);   // 2
        }
        else {
            chunk = previous[previous.length -1];   // 3
        }
        chunk.push(current);   // 4
        return previous;   // 5
    }, []);   // 6
}

console.log(chunk(2, ['a', 'b', 'c', 'd', 'e']));
// prints [ [ 'a', 'b' ], [ 'c', 'd' ], [ 'e' ] ]

Spiegazione di ciascuno di cui // nbrsopra:

  1. Crea un nuovo blocco se il valore precedente, ovvero l'array di blocchi precedentemente restituito, è vuoto o se l'ultimo blocco precedente contiene chunkSizeelementi
  2. Aggiungi il nuovo blocco alla matrice di blocchi esistenti
  3. Altrimenti, il blocco corrente è l'ultimo pezzo dell'array di blocchi
  4. Aggiungi il valore corrente al blocco
  5. Restituisce l'array modificato di blocchi
  6. Inizializza la riduzione passando un array vuoto

Currying basato su chunkSize:

var chunk3 = function(array) {
    return chunk(3, array);
};

console.log(chunk3(['a', 'b', 'c', 'd', 'e']));
// prints [ [ 'a', 'b', 'c' ], [ 'd', 'e' ] ]

È possibile aggiungere la chunk()funzione Arrayall'oggetto globale :

Object.defineProperty(Array.prototype, 'chunk', {
    value: function(chunkSize) {
        return this.reduce(function(previous, current) {
            var chunk;
            if (previous.length === 0 || 
                    previous[previous.length -1].length === chunkSize) {
                chunk = [];
                previous.push(chunk);
            }
            else {
                chunk = previous[previous.length -1];
            }
            chunk.push(current);
            return previous;
        }, []);
    }
});

console.log(['a', 'b', 'c', 'd', 'e'].chunk(4));
// prints [ [ 'a', 'b', 'c' 'd' ], [ 'e' ] ]


7
in coffeescript:

b = (a.splice(0, len) while a.length)

demo 
a = [1, 2, 3, 4, 5, 6, 7]

b = (a.splice(0, 2) while a.length)
[ [ 1, 2 ],
  [ 3, 4 ],
  [ 5, 6 ],
  [ 7 ] ]

a.splice (0, 2) rimuove il subarray di un [0..1] da a e restituisce il subarray a [0..1]. Sto realizzando una serie di tutti quegli array
Arpit Jain,

2
Consiglio di usare il metodo slice () non distruttivo invece di splice ()
Ash Blue,

7
results = []
chunk_size = 10
while(array.length > 0){
   results.push(array.splice(0, chunk_size))
}

1
Non sono sicuro del motivo per cui questo è stato votato in negativo, ma il codice potrebbe usare alcune spiegazioni.
jpaugh

2
Perché la giunzione è distruttiva per l'array originale.
metalim,

7

Il seguente approccio ES2015 funziona senza la necessità di definire una funzione e direttamente su array anonimi (esempio con dimensione del blocco 2):

[11,22,33,44,55].map((_, i, all) => all.slice(2*i, 2*i+2)).filter(x=>x.length)

Se vuoi definire una funzione per questo, puoi farlo come segue (migliorando il commento di K ._ sulla risposta di Blazemonger ):

const array_chunks = (array, chunk_size) => array
    .map((_, i, all) => all.slice(i*chunk_size, (i+1)*chunk_size))
    .filter(x => x.length)

6

E questo sarebbe il mio contributo a questo argomento. Immagino .reduce()sia il modo migliore.

var segment = (arr, n) => arr.reduce((r,e,i) => i%n ? (r[r.length-1].push(e), r)
                                                    : (r.push([e]), r), []),
        arr = Array.from({length: 31}).map((_,i) => i+1);
        res = segment(arr,7);
console.log(JSON.stringify(res));

L'implementazione di cui sopra non è molto efficiente poiché .reduce()esegue tutte le arrfunzioni. Un approccio più efficiente (molto vicino alla soluzione imperativa più veloce) sarebbe, iterando sull'array ridotto (da tagliare) poiché possiamo calcolare in anticipo le sue dimensioni Math.ceil(arr/n);. Una volta che abbiamo l'array di risultati vuoto come Array(Math.ceil(arr.length/n)).fill();il resto è mappare sezioni arrdell'array in esso.

function chunk(arr,n){
  var r = Array(Math.ceil(arr.length/n)).fill();
  return r.map((e,i) => arr.slice(i*n, i*n+n));
}

arr = Array.from({length: 31},(_,i) => i+1);
res = chunk(arr,7);
console.log(JSON.stringify(res));


5

Usa un pezzo di lodash

lodash.chunk(arr,<size>).forEach(chunk=>{
  console.log(chunk);
})

5

Un'altra soluzione usando arr.reduce():

const chunk = (arr, size) => (
  arr.reduce((acc, _, i) => {
    if (i % size === 0) acc.push(arr.slice(i, i + size))
    return acc
  }, [])
)

// Usage:
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
const chunked = chunk(numbers, 3)
console.log(chunked)

Questa soluzione è molto simile alla soluzione di Steve Holgado . Tuttavia, poiché questa soluzione non utilizza la diffusione di array e non crea nuovi array nella funzione di riduzione, è più veloce (vedi test jsPerf ) e soggettivamente più leggibile (sintassi più semplice) rispetto all'altra soluzione.

Ad ogni ennesima iterazione (dove n = size; a partire dalla prima iterazione), l'accumulatore array ( acc) viene aggiunto con un pezzo dell'array ( arr.slice(i, i + size)) e quindi restituito. In altre iterazioni, l'array di accumulatori viene restituito così com'è.

Se sizeè zero, il metodo restituisce un array vuoto. Se sizeè negativo, il metodo restituisce risultati non funzionanti. Quindi, se necessario nel tuo caso, potresti voler fare qualcosa su sizevalori negativi o non positivi .


Se la velocità è importante nel tuo caso, un semplice forciclo sarebbe più veloce dell'uso arr.reduce()(vedi il test jsPerf ) e alcuni potrebbero trovare anche questo stile più leggibile:

function chunk(arr, size) {
  // This prevents infinite loops
  if (size < 1) throw new Error('Size must be positive')

  const result = []
  for (let i = 0; i < arr.length; i += size) {
    result.push(arr.slice(i, i + size))
  }
  return result
}

4

Per una soluzione funzionale, usando Ramda :

Dov'è il popularProductstuo array di input, 5è la dimensione del blocco

import splitEvery from 'ramda/src/splitEvery'

splitEvery(5, popularProducts).map((chunk, i) => {
// do something with chunk

})


4

Approccio a una linea ES6 basato su Array.prototype reducee pushmetodi:

const doChunk = (list, size) => list.reduce((r, v) =>
  (!r.length || r[r.length - 1].length === size ?
    r.push([v]) : r[r.length - 1].push(v)) && r
, []);

console.log(doChunk([0,1,2,3,4,5,6,7,8,9,10,11,12], 5));
// [[0, 1, 2, 3, 4], [5, 6, 7, 8, 9], [10, 11, 12]]

4

Versione del generatore ES6

function* chunkArray(array,size=1){
    var clone = array.slice(0);
    while (clone.length>0) 
      yield clone.splice(0,size); 
};
var a = new Array(100).fill().map((x,index)=>index);
for(const c of chunkArray(a,10)) 
    console.log(c);

Usa constinvece.
Константин Ван,

4

Questa è la soluzione più efficiente e diretta che mi viene in mente:

function chunk(array, chunkSize) {
    let chunkCount = Math.ceil(array.length / chunkSize);
    let chunks = new Array(chunkCount);
    for(let i = 0, j = 0, k = chunkSize; i < chunkCount; ++i) {
        chunks[i] = array.slice(j, k);
        j = k;
        k += chunkSize;
    }
    return chunks;
}

4

ES6 diffonde funzionale #ohmy #ftw

const chunk =
  (size, xs) => 
    xs.reduce(
      (segments, _, index) =>
        index % size === 0 
          ? [...segments, xs.slice(index, index + size)] 
          : segments, 
      []
    );

console.log( chunk(3, [1, 2, 3, 4, 5, 6, 7, 8]) );


4

Ecco una soluzione ricorsiva che è l'ottimizzazione delle chiamate di coda.

const splitEvery = (n, xs, y=[]) =>
  xs.length===0 ? y : splitEvery(n, xs.slice(n), y.concat([xs.slice(0, n)])) 

console.log(splitEvery(2, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]))


2

EDIT: @ mblase75 ha aggiunto un codice più conciso alla risposta precedente mentre stavo scrivendo la mia, quindi consiglio di andare con la sua soluzione.

È possibile utilizzare il codice in questo modo:

var longArray = ["Element 1","Element 2","Element 3", /*...*/];
var smallerArrays = []; // will contain the sub-arrays of 10 elements each
var arraySize = 10;
for (var i=0;i<Math.ceil(longArray.length/arraySize);i++) {
    smallerArrays.push(longArray.slice(i*arraySize,i*arraySize+arraySize));
}

Modificare il valore di arraySizeper modificare la lunghezza massima delle matrici più piccole.


2

Ecco una soluzione non mutante che utilizza solo ricorsione e slice ().

const splitToChunks = (arr, chunkSize, acc = []) => (
    arr.length > chunkSize ?
        splitToChunks(
            arr.slice(chunkSize),
            chunkSize,
            [...acc, arr.slice(0, chunkSize)]
        ) :
        [...acc, arr]
);

Quindi semplicemente usalo come splitToChunks([1, 2, 3, 4, 5], 3)per ottenere [[1, 2, 3], [4, 5]].

Ecco un violino da provare: https://jsfiddle.net/6wtrbx6k/2/

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.