style body attribute javascript arrays split

javascript - body - Dividir matriz en trozos



title css (30)

Digamos que tengo una matriz de Javascript con el siguiente aspecto:

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

¿Qué enfoque sería apropiado para dividir (dividir) el arreglo en muchos arreglos más pequeños con, digamos, 10 elementos como máximo?


Acabo de escribir esto con la ayuda de una función groupBy.

// utils const group = (source) => ({ by: (grouping) => { const groups = source.reduce((accumulator, item) => { const name = JSON.stringify(grouping(item)); accumulator[name] = accumulator[name] || []; accumulator[name].push(item); return accumulator; }, {}); return Object.keys(groups).map(key => groups[key]); } }); const chunk = (source, size) => group(source.map((item, index) => ({ item, index }))) .by(x => Math.floor(x.index / size)) .map(x => x.map(v => v.item)); // 103 items const arr = [6,2,6,6,0,7,4,9,3,1,9,6,1,2,7,8,3,3,4,6,8,7,6,9,3,6,3,5,0,9,3,7,0,4,1,9,7,5,7,4,3,4,8,9,0,5,1,0,0,8,0,5,8,3,2,5,6,9,0,0,1,5,1,7,0,6,1,6,8,4,9,8,9,1,6,5,4,9,1,6,6,1,8,3,5,5,7,0,8,3,1,7,1,1,7,6,4,9,7,0,5,1,0]; const chunks = chunk(arr, 10); console.log(JSON.stringify(chunks));


Aquí está la implementación ordenada y optimizada de la función chunk() . Suponiendo que el tamaño de fragmento predeterminado es 10 .

var chunk = function(list, chunkSize) { if (!list.length) { return []; } if (typeof chunkSize === undefined) { chunkSize = 10; } var i, j, t, chunks = []; for (i = 0, j = list.length; i < j; i += chunkSize) { t = list.slice(i, i + chunkSize); chunks.push(t); } return chunks; }; //calling function var list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]; var chunks = chunk(list);


Aquí está mi enfoque utilizando la comprensión de la lista de Coffeescript. Un gran artículo que detalla las comprensiones en Coffeescript se puede encontrar aquí .

chunk: (arr, size) -> chunks = (arr.slice(index, index+size) for item, index in arr by size) return chunks


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 splitToChunks([1, 2, 3, 4, 5], 3) como splitToChunks([1, 2, 3, 4, 5], 3) para obtener [[1, 2, 3], [4, 5]] .

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


Aquí hay una versión de ES6 usando reducir

perChunk = 2 // items per chunk inputArray = [''a'',''b'',''c'',''d'',''e''] 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 }, []) // result: [[''a'',''b''], [''c'',''d''], [''e'']]

Y ya 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 rociar un poco de concat en la mezcla para el mismo resultado final:

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


Cambié un poco de BlazeMonger para usarlo con un objeto jQuery ...

var $list = $(''li''), $listRows = []; for (var i = 0, len = $list.length, chunk = 4, n = 0; i < len; i += chunk, n++) { $listRows[n] = $list.slice(i, i + chunk); }



Creo que esta es una buena solución recursiva con la sintaxis de ES6:

# in coffeescript # assume "ar" is the original array # newAr is the new array of arrays newAr = [] chunk = 10 for i in [0... ar.length] by chunk newAr.push ar[i... i+chunk] # or, print out the elements one line per chunk for i in [0... ar.length] by chunk console.log ar[i... i+chunk].join '' ''


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

Podrías usar 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 arraySize para cambiar la longitud máxima de las matrices más pequeñas.


El enfoque de una línea de ES6 basado en el método Array.prototype reduce y push :

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


El método array.slice puede extraer una porción desde el principio, la mitad o el final de una matriz para cualquier propósito que requiera, 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 }


Esta debería ser una respuesta directa sin muchas complicaciones matemáticas.

function chunkArray(array, sizeOfTheChunkedArray) { const chunked = []; for (let element of array) { const last = chunked[chunked.length - 1]; if(!last || last.length === sizeOfTheChunkedArray) { chunked.push([element]) } else { last.push(element); } } return chunked; }


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


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 ningún resto, entonces el índice es divisible por el tamaño del fragmento, así que tome una porción de la matriz original (comenzando en el índice actual) y agréguela a la matriz del acumulador.

Por lo tanto, la matriz del acumulador devuelto para cada iteración de reducir se ve algo como esto:

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


Hola prueba esto

function split(arr, howMany) { var newArr = []; start = 0; end = howMany; for(var i=1; i<= Math.ceil(arr.length / howMany); i++) { newArr.push(arr.slice(start, end)); start = start + howMany; end = end + howMany } console.log(newArr) } split([1,2,3,4,55,6,7,8,8,9],3)


Hoy en día, puede utilizar la función Chunk de lodash para dividir la matriz en matrices más pequeñas https://lodash.com/docs#chunk Ya no es necesario jugar con los bucles!


Intente evitar la suciedad 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, vea la charla de Andrew Dupont sobre JSConf 2011, "Todo está permitido: extensión incorporada" , 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;


Mi objetivo era crear una solución no mutante simple 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 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 :-)


Modificado de una respuesta por dbaseman: https://.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)]; }) ); } });

Manifestación:

> [1,2,3,4,5,6,7].chunk_inefficient(3) [[1,2,3],[4,5,6],[7]]

Adición menor :

Debo señalar que lo anterior es una solución no muy 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 a continuación, pero quizás un factor constante peor debido a la construcción de listas vacías. Uno podría reescribir esto de la siguiente manera (en su mayoría lo mismo que el método de Blazemonger, por lo que originalmente no presenté esta respuesta):

Método más eficiente:

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; } }); // refresh page if experimenting and you already defined Array.prototype.chunk

Mi forma preferida en la actualidad 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 quieres 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)); }});


Ok, vamos a empezar con uno bastante apretado:

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 función reductora apretada:

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 vinculamos this con un número, podemos hacer un 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),[]);

Entonces la función todavía bastante apretada 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);


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


Prefiero usar el método de splice :

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


Probé las diferentes respuestas en jsperf.com. El resultado está disponible allí: http://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; }


Si usa la versión EcmaScript> = 5.1, puede implementar una versión funcional de chunk() utilizando 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 // nbr arriba:

  1. Cree un nuevo fragmento si el valor anterior, es decir, la matriz de fragmentos devueltos anteriormente, está vacío o si el último fragmento anterior tiene elementos de chunkSize
  2. Agregue el nuevo trozo a la matriz de trozos existentes
  3. De lo contrario, el fragmento actual es el último fragmento del conjunto de fragmentos.
  4. Agregue el valor actual a la porción
  5. Devuelve la matriz modificada de trozos
  6. Inicializa la reducción pasando una matriz vacía

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

Puedes agregar la función chunk() al objeto Array 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'' ] ]


Versión del Generator 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);


Vieja pregunta: ¡Nueva respuesta! ¡De hecho, estaba trabajando con una respuesta de esta pregunta e hice que un amigo la mejorara! 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]]


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 a través de toda la función arr . Un enfoque más eficiente (muy cercano a la solución imperativa más rápida) sería, iterando sobre la matriz reducida (a ser fragmentada), ya que podemos calcular su tamaño por adelantado por Math.ceil(arr/n); . Una vez que tengamos la matriz de resultados vacía como Array(Math.ceil(arr.length/n)).fill(); el resto es mapear segmentos de la matriz arr en él.

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


JSFiddle el siguiente JSFiddle para demostrar mi enfoque a su pregunta.

(function() { // Sample arrays var //elements = ["0", "1", "2", "3", "4", "5", "6", "7"], elements = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "40", "41", "42", "43"]; var splitElements = [], delimiter = 10; // Change this value as needed // parameters: array, number of elements to split the array by if(elements.length > delimiter){ splitElements = splitArray(elements, delimiter); } else { // No need to do anything if the array''s length is less than the delimiter splitElements = elements; } //Displaying result in console for(element in splitElements){ if(splitElements.hasOwnProperty(element)){ console.log(element + " | " + splitElements[element]); } } })(); function splitArray(elements, delimiter) { var elements_length = elements.length; if (elements_length > delimiter) { var myArrays = [], // parent array, used to store each sub array first = 0, // used to capture the first element in each sub array index = 0; // used to set the index of each sub array for (var i = 0; i < elements_length; ++i) { if (i % delimiter === 0) { // Capture the first element of each sub array from the original array, when i is a modulus factor of the delimiter. first = i; } else if (delimiter - (i % delimiter) === 1) { // Build each sub array, from the original array, sliced every time the i one minus the modulus factor of the delimiter. index = (i + 1) / delimiter - 1; myArrays[index] = elements.slice(first, i + 1); } else if(i + 1 === elements_length){ // Build the last sub array which contain delimiter number or less elements myArrays[index + 1] = elements.slice(first, i + 1); } } // Returned is an array of arrays return myArrays; } }

En primer lugar, tengo dos ejemplos: una matriz con menos de ocho elementos, otra con una matriz con más de ocho elementos (comente cualquiera que no quiera usar).

Luego verifico el tamaño de la matriz, simple pero esencial para evitar cálculos adicionales. Desde aquí, si la matriz cumple con los criterios (tamaño de matriz> delimiter ), pasamos a la función splitArray .

La función splitArray toma el delimitador (es decir, 8, ya que es lo que quiere dividir) y la matriz en sí. Como estamos reutilizando mucho la longitud del arreglo, lo estoy almacenando en una variable, así como la first y la last .

first representa la posición del primer elemento en una matriz. Esta matriz es una matriz hecha de 8 elementos. Entonces, para determinar el primer elemento, usamos el operador de módulo .

myArrays es el conjunto de matrices. En él almacenaremos en cada índice, cualquier sub array de tamaño 8 o inferior. Esta es la estrategia clave en el algoritmo a continuación.

index representa el índice para la variable myArrays . Cada vez que se almacena una matriz secundaria de 8 elementos o menos, debe almacenarse en el índice correspondiente. Entonces si tenemos 27 elementos, eso significa 4 matrices. La primera, segunda y tercera matriz tendrán 8 elementos cada una. El último tendrá solo 3 elementos. Entonces, el index será 0, 1, 2 y 3 respectivamente.

La parte difícil es simplemente calcular las matemáticas y optimizarlas lo mejor posible. Por ejemplo else if (delimiter - (i % delimiter) === 1) esto es para encontrar el último elemento que debe ir en la matriz, cuando una matriz estará llena (ejemplo: contiene 10 elementos).

Este código funciona para todos los escenarios, incluso puede cambiar el delimiter para que coincida con cualquier tamaño de matriz que desee obtener. Muy dulce derecha :-)

¿Alguna pregunta? No dude en preguntar en los comentarios a continuación.


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


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