Dividir la matriz en trozos

516

Digamos que tengo una matriz Javascript que se ve de la siguiente manera:

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

¿Qué enfoque sería apropiado para fragmentar (dividir) la matriz en muchas matrices más pequeñas con, digamos, 10 elementos como máximo?

Industrial
fuente
Posible duplicado de dividir una matriz JS en N matrices
TJ
24
Para usuarios lodash , estás buscando _.chunk .
Ulysse BN
2
¡Acabo de probar lo const chunk = (arr, n) => arr.length ? [arr.slice(0, n), ...chunk(arr.slice(n), n)] : []que es bueno y corto pero parece tomar alrededor de 256 × el tiempo que la respuesta de @ AymKdn para 1,000 elementos, y 1,058 × tanto como para 10,000 elementos!
Toph
si necesita un tamaño mínimo del último fragmento también, estas son las opciones: stackoverflow.com/questions/57908133/…
Ahmet Cetin el

Respuestas:

643

El método array.slice puede extraer un segmento desde el principio, el medio o el final de una matriz para cualquier propósito que necesite, sin cambiar la matriz original.

var i,j,temparray,chunk = 10;
for (i=0,j=array.length; i<j; i+=chunk) {
    temparray = array.slice(i,i+chunk);
    // do whatever
}
Blazemonger
fuente
19
Recuerde si esta es una función util para afirmar contra el chunkser 0. (bucle infinito)
Steven Lu
19
No, el último fragmento debería ser más pequeño que los demás.
Blazemonger
77
@Blazemonger, de hecho! La próxima vez lo intentaré yo mismo antes de llegar a conclusiones. Supuse (incorrectamente) que pasar una entrada a array.slice que excedía los límites de la matriz sería un problema, ¡pero funciona perfectamente!
rysqui
55
Para una sola línea (cadena-amantes) : 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
¿Por qué necesitas j? Primero pensé que era una optimización, pero en realidad es más lenta que para (i = 0; i <array.length; i ++) {}
Alex
149

Modificado de una respuesta de 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]]


anexo menor :

Debo señalar que lo anterior es una solución no tan elegante (en mi opinión) para usar Array.map. Básicamente hace lo siguiente, donde ~ es concatenación:

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

Tiene el mismo tiempo de ejecución asintótico que el método siguiente, pero quizás un factor constante peor debido a la creación de listas vacías. Uno podría reescribir esto de la siguiente manera (principalmente el mismo que el método de Blazemonger, por lo que originalmente no envié esta respuesta):

Método más eficiente:

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


Mi forma preferida hoy en día es la anterior, o una de las siguientes:

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

  }
});

Manifestación:

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

O si no desea una función Array.range, en realidad es solo una línea (excluyendo la pelusa):

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));
}});
ninjagecko
fuente
41
Eh, evitaría jugar con el prototipo ya que la sensación de frialdad que obtienes al llamar a la chunkfunción en la matriz realmente no supera la complejidad adicional que estás agregando y los errores sutiles que pueden causar los juegos con los prototipos incorporados.
Gordon Gustafson
12
Él no está jugando con ellos, los está extendiendo para Arrays. Entiendo que nunca toques Object.prototype porque eso burbujearía a todos los objetos (todo) pero para esta función específica de Array no veo ningún problema.
rlemon
Sin embargoarray.map(function(i)array.map(function(elem,i)
Nigel Angel
3
Basado en la tabla de compatibilidad en el sitio de desarrollo de mozilla, Array.map para IE9 +. Ten cuidado.
Maikel D
Tenga cuidado de pasar flotando - 7.5 tipo de números - conducir a trozos impredecibles
Gal Bracha
103

Aquí hay una versión ES6 que usa reducir

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']]

Y está listo para encadenar más mapas / reducir transformaciones. Su matriz de entrada se deja intacta


Si prefiere una versión más corta pero menos legible, puede espolvorear un poco concaten la mezcla para obtener el mismo resultado final:

inputArray.reduce((all,one,i) => {
   const ch = Math.floor(i/perChunk); 
   all[ch] = [].concat((all[ch]||[]),one); 
   return all
}, [])
Andrei R
fuente
Esta parece ser la solución más condensada. ¿Qué obtiene chunkIndex = Math.floor (index / perChunk)? ¿Es el promedio?
yo-yo
5/2 = 2.5y Math.floor(2.5) = 2entonces el artículo con el índice 5 se colocará en el cubo 2
Andrei R
Gracias Andrei R. Ah, así que pasa de 0 a 2. Entonces, ¿cómo se llama esto en términos matemáticos? Creo que mi punto es que nunca hubiera pensado dividir index / 2 cada índice para obtener el índice de cada segmento. Así que estoy tratando de entenderlo porque realmente me gusta, pero no lo entiendo completamente en términos matemáticos. Por lo general, hago esto para obtener promedios de un número total, pero esto se ve diferente.
yo-yo
Esta solución es ineficiente en comparación con otras soluciones porque necesita iterar sobre cada elemento.
JP de la Torre
tienes razón @JPdelaTorre, probablemente no sea tan eficiente como las soluciones que cortan matrices, pero aquí estás dividiendo pelos. la mayoría de las respuestas enumeradas serían ineficientes según esa definición.
Andrei R
102

Intente evitar mucking con prototipos nativos, incluido Array.prototype, si no sabe quién consumirá su código (terceros, compañeros de trabajo, usted mismo en una fecha posterior, etc.).

Hay formas de extender los prototipos de manera segura (pero no en todos los navegadores) y hay formas de consumir de manera segura los objetos creados a partir de prototipos extendidos, pero una mejor regla general es seguir el Principio de Menos Sorpresa y evitar estas prácticas por completo.

Si tiene algo de tiempo, mire la charla JSConf 2011 de Andrew Dupont, "Todo está permitido: ampliación de los elementos integrados" , para una buena discusión sobre este tema.

Pero volviendo a la pregunta, si bien las soluciones anteriores funcionarán, son demasiado complejas y requieren una sobrecarga computacional innecesaria. Aquí está mi solución:

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;
furf
fuente
25
"evite mucking con prototipos nativos" los nuevos desarrolladores de js deberían hacerse un tatuaje temporal, si no permanente, de esta frase.
Jacob Dalton
2
He estado usando JavaScript durante años y casi nunca me molesto en prototipar, en la mayoría de las funciones de llamada, nunca modifico como ves que hacen algunas personas.
1984
2
La mejor sugerencia en mi opinión, la más simple de entender y en la implementación, ¡muchas gracias!
Olga Farber
1
@JacobDalton Creo que es culpa de todas las universidades. La gente piensa que OOP debe usarse en todas partes. Entonces tienen miedo de "simplemente crear una función". Quieren asegurarse de ponerlo dentro de algo. Incluso si no es apropiado en absoluto. Si no hay notación de punto, no hay "arquitectura".
Gherman
51

Probé las diferentes respuestas en jsperf.com. El resultado está disponible allí: https://web.archive.org/web/20150909134228/https://jsperf.com/chunk-mtds

Y la función más rápida (y que funciona desde IE8) es esta:

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;
}
AymKdn
fuente
1
Gracias @AymKdn por hacer este punto de referencia: ¡Esto fue muy útil! Estaba usando el enfoque de empalme y se bloqueó mi navegador Chrome v70 en un tamaño de fragmento de 884432. Con su enfoque sugerido de "corte" en su lugar, mi código ya no bloquea el proceso de "procesamiento" del navegador. :)
Benny Neugebauer el
Aquí hay una versión mecanografiada de esto: 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

Prefiero usar el método de empalme :

var chunks = function(array, size) {
  var results = [];
  while (array.length) {
    results.push(array.splice(0, size));
  }
  return results;
};
Arek Flinik
fuente
16
Debe tener cuidado con esta solución de empalme ya que modifica la matriz original, así que asegúrese de documentar claramente los efectos secundarios.
bdrx
3
Luego use el segmento en su lugar
mplungjan
@mplungjan El resultado sería la misma matriz una y otra vez cuando se utiliza slice. Por lo tanto, no es realmente un reemplazo directo sin algunas modificaciones más.
nietonfir
Lo único que agregaría a esta respuesta es un clon de la matriz original. Lo haría con el operador de propagación de ES6. var clone = [...array]luego verifique la longitud y empalme sobre esa matriz clonada.
MauricioLeal
1
O si no puede usar las funciones de ES6, simplemente puede array = array.slice()crear una copia superficial.
3limin4t0r
34

One-liner en 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))
Shairon Toledo
fuente
99
modifica la listmatriz original
Jacka
66
Solución fácil usando .slice () .. .map((_,i) => list.slice(i*chuckSize,i*chuckSize+chuckSize))
James Robey
2
En JSPerf, esto es drásticamente más eficaz que muchas de las otras respuestas.
Micah Henning
30

Vieja pregunta: nueva respuesta! ¡De hecho, estaba trabajando con una respuesta de esta pregunta y un amigo mejoró! Asi que aqui esta:

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]]
rlemon
fuente
14
para su información, el rendimiento de este método es O (N ^ 2), por lo que no debe usarse en secciones de código críticas para el rendimiento o con matrices largas (específicamente, cuando la matriz .lengthes mucho mayor que el tamaño del fragmento n). Si se tratara de un lenguaje vago (a diferencia de javascript), este algoritmo no sufriría el tiempo O (N ^ 2). Dicho esto, la implementación recursiva es elegante. Probablemente pueda modificarlo para mejorar el rendimiento definiendo primero una función auxiliar que se repita array,positiony luego despachando: Array.prototype.chunkdevuelve [su función auxiliar] (...)
ninjagecko
gracias sensai por bendecirme con tu espíritu que debo haber canalizado esta noche
mordido el
1
o ...var chunk = (arr, n) => { if ( !arr.length ) return []; return [ arr.slice( 0, n ) ].concat( chunk(arr.slice(n), n) ) }
Ed Williams
28

Hoy en día puede usar la función lodash 'chunk para dividir el conjunto en conjuntos más pequeños https://lodash.com/docs#chunk ¡ Ya no es necesario jugar con los bucles!

George Herolyants
fuente
3
Siento que debería haber un descargo de responsabilidad para las preguntas de SO javascript: ¿has probado lodash? Más o menos lo primero que incluyo en el nodo o el navegador.
Jacob Dalton el
20

Ha habido muchas respuestas pero esto es lo que 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]]

Primero, verifique si hay un resto al dividir el índice por el tamaño del fragmento.

Si hay un resto, simplemente devuelva la matriz del acumulador.

Si no hay resto, entonces el índice es divisible por el tamaño del fragmento, por lo tanto, tome un corte de la matriz original (comenzando en el índice actual) y agréguelo a la matriz del acumulador.

Entonces, la matriz de acumulador devuelta para cada iteración de reducción se ve así:

// 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]]
Steve Holgado
fuente
Buena solución y buena representación visual de las iteraciones. Terminé con una solución muy similar que
publiqué
15

Usando generadores

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

Ikechukwu Eze
fuente
3
Me sorprendieron todas las respuestas a esta pregunta que casi usaban generadores o las usaban de formas más complicadas. Buena brevedad y rendimiento con esta solución.
Keith
14

Ok, comencemos con uno bastante ajustado:

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

Que se usa así:

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

Entonces tenemos esta estrecha función reductora:

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

Que se usa así:

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

Dado que un gatito muere cuando nos unimos thisa un número, podemos hacer curry manual como este:

// 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;
   };
}

Que se usa así:

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

Luego, la función todavía bastante ajustada que lo hace todo de una vez:

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);
user239558
fuente
No funciona en iE8.
Nadeemmnn Mohd
44
¡DECIR AH! Me encanta el comentario del gatito. pena por ninguna entrada adicional constructiva :)
29er
Lo haría (p[i/n|0] || (p[i/n|0] = [])), por lo que no se asigna un valor, si no es necesario ...
yckart
12

Apunté a crear una solución simple no mutante en ES6 puro. Las peculiaridades en javascript hacen que sea necesario llenar la matriz vacía antes de mapear :-(

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

Esta versión con recursión parece más simple y más convincente:

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

Las funciones de matriz ridículamente débiles de ES6 hacen buenos rompecabezas :-)

Thoredge
fuente
1
También escribí el mío mucho así. Todavía funciona si se quita el 0del fill, que hace que la fillmirada un poco más sensible, en mi humilde opinión.
Coert Grobbelaar
12

Creo que esta es una buena solución recursiva con la sintaxis 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));

Gergely Fehérvári
fuente
9

Creó un paquete npm para este 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;

Cuando use un TypedArray

var result = [];

for (var i = 0; i < arr.length; i += size) {
  result.push(arr.subarray(i, size + i));
}
return result;
zhiyelee
fuente
@ A1rPun Lo malo, no agregué comentarios allí. Sí, no hay ningún slicemétodo para TypedArray, podemos usar subarrayen su lugar developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
zhiyelee
8

Si usa la versión EcmaScript> = 5.1, puede implementar una versión funcional del chunk()uso de array.reduce () que tiene complejidad 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' ] ]

Explicación de cada uno de los // nbranteriores:

  1. Cree un nuevo fragmento si el valor anterior, es decir, el conjunto de fragmentos devuelto anteriormente, está vacío o si el último fragmento anterior tiene chunkSizeelementos
  2. Agregue el nuevo fragmento al conjunto de fragmentos existentes
  3. De lo contrario, el fragmento actual es el último fragmento del conjunto de fragmentos.
  4. Agregar el valor actual al fragmento
  5. Devuelve la matriz modificada de fragmentos
  6. Inicialice la reducción pasando una matriz vacía

Curry basado en chunkSize:

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

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

Puede agregar la chunk()función al Arrayobjeto global :

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' ] ]

matsev
fuente
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 ] ]
Arpit Jain
fuente
a.splice (0, 2) elimina el subconjunto de a [0..1] de a y devuelve el subconjunto a [0..1]. Estoy haciendo un conjunto de todos esos conjuntos
Arpit Jain
2
Recomiendo usar el método no destructivo slice () en lugar de splice ()
Ash Blue
7
results = []
chunk_size = 10
while(array.length > 0){
   results.push(array.splice(0, chunk_size))
}
Jon
fuente
1
No estoy seguro de por qué esto fue rechazado, pero el código podría usar alguna explicación.
jpaugh
2
Porque el empalme es destructivo para la matriz original.
metalim
7

El siguiente enfoque de ES2015 funciona sin tener que definir una función y directamente en matrices anónimas (ejemplo con tamaño de fragmento 2):

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

Si desea definir una función para esto, puede hacerlo de la siguiente manera (mejorando el comentario de K ._ sobre la respuesta de Blazemonger ):

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

Y esta sería mi contribución a este tema. Supongo que .reduce()es la mejor manera.

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

Pero la implementación anterior no es muy eficiente ya que .reduce()ejecuta todas las arrfunciones. Sería un enfoque más eficiente (muy cercano a la solución imperativa más rápida), iterar sobre la matriz reducida (para ser fragmentada) ya que podemos calcular su tamaño por adelantado Math.ceil(arr/n);. Una vez que tengamos la matriz de resultados vacía, Array(Math.ceil(arr.length/n)).fill();el resto es mapear rebanadas de la arrmatriz en ella.

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

Redu
fuente
5

Usa trozos de lodash

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

Una solución más 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)

Esta solución es muy similar a la solución de Steve Holgado . Sin embargo, debido a que esta solución no utiliza la distribución de la matriz y no crea nuevas matrices en la función reductora, es más rápida (ver prueba jsPerf ) y subjetivamente más legible (sintaxis más simple) que la otra solución.

En cada enésima iteración (donde n = size; comenzando en la primera iteración), la matriz del acumulador ( acc) se agrega con un fragmento de la matriz (arr.slice(i, i + size) ) y luego se devuelve. En otras iteraciones, la matriz del acumulador se devuelve tal cual.

Si sizees cero, el método devuelve una matriz vacía. Si sizees negativo, el método devuelve resultados rotos. Entonces, si es necesario en su caso, es posible que desee hacer algo sobre sizevalores negativos o no positivos .


Si la velocidad es importante en su caso, un forbucle simple sería más rápido que usarlo arr.reduce()(vea la prueba jsPerf ), y algunos también pueden encontrar este estilo más legible:

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
}
mts knn
fuente
4

Para una solución funcional, usando Ramda :

¿Dónde popularProductsestá su matriz de entrada, 5es el tamaño del fragmento

import splitEvery from 'ramda/src/splitEvery'

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

})

Damian Green
fuente
4

Enfoque de una línea ES6 basado en Array.prototype reducey pushmétodos:

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]]
dhilt
fuente
4

Versión del generador 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);
Rm558
fuente
Usar en su constlugar.
Константин Ван
4

Esta es la solución más eficiente y directa que se me ocurre:

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;
}
mpen
fuente
4

ES6 se extiende funcional #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]) );

goofballLogic
fuente
4

Aquí hay una solución recursiva que es la optimización de la llamada de cola.

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

Chris Vouga
fuente
2

EDITAR: @ ​​mblase75 agregó un código más conciso a la respuesta anterior mientras escribía el mío, por lo que recomiendo ir con su solución.

Podrías usar un código como este:

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

Cambie el valor de arraySizepara cambiar la longitud máxima de las matrices más pequeñas.

dentaku
fuente
2

Aquí hay una solución no mutante que usa solo recursión y slice ().

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

Luego, simplemente úsalo splitToChunks([1, 2, 3, 4, 5], 3)para obtener[[1, 2, 3], [4, 5]] .

Aquí hay un violín que puede probar: https://jsfiddle.net/6wtrbx6k/2/

Suhair Zain
fuente