Dividir matriz em pedaços

516

Digamos que eu tenho uma matriz Javascript parecida com a seguinte:

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

Que abordagem seria apropriada para dividir (dividir) a matriz em muitas matrizes menores com, digamos, 10 elementos no máximo?

Industrial
fonte
Possível duplicata de divisão de uma matriz JS em N matrizes
TJ
24
Para usuários do lodash , você está procurando _.chunk .
Ulysse BN
2
Eu apenas tentei o const chunk = (arr, n) => arr.length ? [arr.slice(0, n), ...chunk(arr.slice(n), n)] : []que é bom e curto, mas parece demorar cerca de 256 × enquanto a resposta do @ AymKdn para 1.000 elementos e 1.058 × para 10.000 elementos!
Toph
se você precisa de tamanho mínimo da última parcela, também, aqui estão as opções: stackoverflow.com/questions/57908133/...
Ahmet Cetin

Respostas:

641

O método array.slice pode extrair uma fatia do início, meio ou fim de uma matriz para qualquer finalidade que você precisar, sem alterar a 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
fonte
19
Lembre-se se esta é uma função util para afirmar contra o chunkser 0. (loop infinito)
Steven Lu
19
Não, o último pedaço deve ser menor que os outros.
Blazemonger
7
@Blazemonger, de fato! Da próxima vez, eu mesmo tentarei antes de tirar conclusões. Eu assumi (incorretamente) que passar uma entrada para array.slice que excedeu os limites da matriz seria um problema, mas funciona perfeitamente!
Rysqui
55
Para uma das linhas (cadeia-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 que você precisa de j? Primeiro, pensei que fosse uma otimização, mas na verdade é mais lenta do que para (i = 0; i <array.length; i ++) {} #
Alex Alex
149

Modificado de uma resposta por 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]]


adenda menor :

Devo salientar que o exposto acima é uma solução alternativa não muito elegante (em minha opinião) para usar Array.map. Basicamente, faz o seguinte, onde ~ é concatenação:

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

Ele tem o mesmo tempo de execução assintótico que o método abaixo, mas talvez seja um fator constante pior devido à criação de listas vazias. Pode-se reescrever isso da seguinte maneira (principalmente o mesmo método do Blazemonger, razão pela qual eu não enviei originalmente esta resposta):

Método mais 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)
)


Minha maneira preferida atualmente é a acima, ou uma das seguintes opções:

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

Ou, se você não deseja uma função Array.range, na verdade é apenas uma linha (excluindo o buço):

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

ou

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
fonte
41
Eh, eu evitaria mexer com o protótipo, pois a sensação de frescura que você obtém ao chamar a chunkfunção na matriz realmente não supera a complexidade extra que você está adicionando e os erros sutis que mexer com protótipos embutidos pode causar.
Gordon Gustafson
12
Ele não está mexendo com eles, está estendendo-os para Arrays. Eu entendo nunca tocar em Object.prototype, porque isso borbulharia para todos os objetos (tudo), mas para esta função específica de matriz não vejo nenhum problema.
Rlemon
Certeza que deve ser array.map(function(i)não array.map(function(elem,i)embora
Nigel Anjo
3
Com base na tabela de compatibilidade no site de desenvolvimento do mozilla, Array.map for for IE9 +. Seja cuidadoso.
Mai13 D Mai13
Tenha cuidado para passar números flutuantes - tipo 7,5 - levar a pedaços imprevisíveis #
2019 Gal
103

Aqui está uma versão do ES6 usando o reduzir

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 você está pronto para encadear mais mapas / reduzir transformações. Sua matriz de entrada é deixada intacta


Se você preferir uma versão mais curta, mas menos legível, poderá adicionar um pouco concatda mistura para o mesmo resultado final:

inputArray.reduce((all,one,i) => {
   const ch = Math.floor(i/perChunk); 
   all[ch] = [].concat((all[ch]||[]),one); 
   return all
}, [])
Andrei R
fonte
Esta parece ser a solução mais condensada. O que está recebendo chunkIndex = Math.floor (index / perChunk)? É a média?
me-me
5/2 = 2.5e Math.floor(2.5) = 2assim artigo com índice 5 será colocada no balde 2
Andrei R
Obrigado Andrei R. Ah, então ele passa por 0 - 2. Então, como isso é chamado em termos de matemática? Acho que meu argumento é que nunca pensei em dividir o índice / 2 em cada índice para obter o índice de cada fatia. Então, eu estou tentando entender porque eu realmente gosto, mas não entendo completamente em termos de matemática. Eu costumo fazer isso para obter médias de um número total, mas isso parece diferente.
me-me
Essa solução é ineficiente quando comparada a outras soluções, porque você precisa iterar sobre cada elemento.
JP de la Torre
você está certo @JPdelaTorre, provavelmente não é tão eficiente quanto as soluções que cortam matrizes, mas você está cortando cabelos aqui. a maioria das respostas listadas seria ineficiente por essa definição.
Andrei R
102

Tente evitar mexer com protótipos nativos, incluindo Array.prototype, se você não souber quem consumirá seu código (terceiros, colegas de trabalho, você mesmo posteriormente).

Existem maneiras de estender protótipos com segurança (mas não em todos os navegadores) e maneiras de consumir com segurança objetos criados a partir de protótipos estendidos, mas uma regra prática melhor é seguir o Princípio da menor surpresa e evitar essas práticas por completo.

Se você tiver algum tempo, assista à conversa do JSConf 2011 de Andrew Dupont, "Tudo é permitido: estendendo os incorporados" , para uma boa discussão sobre este tópico.

Mas voltando à questão, embora as soluções acima funcionem, elas são excessivamente complexas e exigem sobrecarga computacional desnecessária. Aqui está a minha solução:

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
fonte
25
"evite mexer com protótipos nativos" Os novos desenvolvedores de js devem fazer uma tatuagem temporária, se não permanente, dessa frase.
Jacob Dalton
2
Eu uso javascript há anos e passo quase um tempo incomodando-me com o protótipo, nas funções mais importantes, nunca modificando como você vê algumas pessoas.
1984
2
a melhor sugestão aos meus olhos, a mais simples de entender e implementar, muito obrigado!
Olga Farber
1
@JacobDalton Eu acho que é tudo culpa das universidades. As pessoas pensam que o POO deve ser usado em qualquer lugar. Então, eles têm medo de "apenas criar uma função". Eles querem ter certeza de colocá-lo dentro de algo. Mesmo que não seja apropriado. Se não houver notação de ponto, não haverá "arquitetura".
Gherman
51

Testei as diferentes respostas em jsperf.com. O resultado está disponível lá: https://web.archive.org/web/20150909134228/https://jsperf.com/chunk-mtds

E a função mais rápida (e que funciona no IE8) é 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
fonte
1
Obrigado @AymKdn por fazer essa referência: isso foi muito útil! Eu estava usando a abordagem de emenda e ele travou meu navegador Chrome v70 em um tamanho de 884432. Com sua abordagem de "fatia" sugerida, meu código não interrompe mais o processo de "renderização" do navegador. :)
Benny Neugebauer
34

Eu preferiria usar o método de emenda :

var chunks = function(array, size) {
  var results = [];
  while (array.length) {
    results.push(array.splice(0, size));
  }
  return results;
};
Arek Flinik
fonte
16
Deve-se ter cuidado com esta solução de emenda, pois ela modifica a matriz original, portanto, certifique-se de documentar claramente os efeitos colaterais.
Bdrx 13/05
3
Em seguida, use a fatia em vez disso
mplungjan
@mplungjan O resultado seria a mesma matriz repetidamente ao usar fatia. Portanto, não é realmente um substituto substituto sem mais algumas modificações.
Nietonfir
A única coisa que eu acrescentaria a esta resposta é um clone da matriz original. Eu faria isso com o operador de spread do ES6. var clone = [...array]faça a verificação e emenda do comprimento sobre essa matriz clonada.
MauricioLeal #
1
Ou, se você não pode usar os recursos do ES6, pode simplesmente array = array.slice()criar uma cópia superficial.
3limin4t0r 23/08/19
34

Uma linha no 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
fonte
9
ele modifica o original listmatriz
Jacka
6
Fácil correção usando .slice () .. .map((_,i) => list.slice(i*chuckSize,i*chuckSize+chuckSize))
James Robey
2
No JSPerf, isso é drasticamente mais eficiente do que muitas das outras respostas.
Micah Henning
30

Pergunta antiga: nova resposta! Na verdade, eu estava trabalhando com uma resposta para essa pergunta e um amigo a melhorou! Então aqui está:

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
fonte
14
fyi, o desempenho desse método é O (N ^ 2), portanto, não deve ser usado em seções críticas de desempenho ou em matrizes longas (especificamente, quando a matriz .lengthé muito maior que o tamanho do pedaço n). Se essa fosse uma linguagem lenta (diferente do javascript), esse algoritmo não sofreria o tempo O (N ^ 2). Dito isto, a implementação recursiva é elegante. Provavelmente, você pode modificá-lo para melhorar o desempenho definindo primeiro uma função auxiliar que recurses em array,position, em seguida, despachando: Array.prototype.chunkretornos [sua função auxiliar] (...)
ninjagecko
graças sensai por me abençoar com seu espírito que eu devo ter canalizados esta noite
mordido
1
ou ...var chunk = (arr, n) => { if ( !arr.length ) return []; return [ arr.slice( 0, n ) ].concat( chunk(arr.slice(n), n) ) }
Ed Williams
28

Atualmente, você pode usar a função chod do lodash 'para dividir a matriz em matrizes menores https://lodash.com/docs#chunk Não é mais necessário mexer nos loops!

George Herolyants
fonte
3
Eu sinto que deve haver um aviso de isenção de responsabilidade para perguntas sobre javascript: você já tentou o lodash? Praticamente a primeira coisa que incluo no nó ou no navegador.
Jacob Dalton
20

Houve muitas respostas, mas é isso que eu 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]]

Primeiro, verifique se há um restante ao dividir o índice pelo tamanho do pedaço.

Se houver um restante, basta retornar a matriz do acumulador.

Se não houver resto, o índice é divisível pelo tamanho do pedaço, então pegue uma fatia da matriz original (iniciando no índice atual) e adicione-a à matriz do acumulador.

Portanto, a matriz do acumulador retornada para cada iteração de redução se parece com isso:

// 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
fonte
Solução agradável e boa representação visual das iterações. Acabei com uma solução muito semelhante, que eu postei como resposta: stackoverflow.com/a/60779547
mts knn
15

Usando geradores

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
fonte
3
Fiquei surpreso com todas as respostas a essa pergunta que estavam quase usando geradores ou de maneiras mais complicadas. Boa concisão e desempenho com esta solução.
21419 Keith
14

Ok, vamos começar com um bastante apertado:

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 é usado assim:

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

Então nós temos esta função redutora apertada:

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

Que é usado assim:

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

Como um gatinho morre quando vinculamos thisa um número, podemos fazer 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 é usado assim:

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

Então a função ainda bastante rígida que faz tudo de uma só 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
fonte
Não funciona no iE8.
Nadeemmnn Mohd
4
HA! eu amo o comentário do gatinho pena de não entrada adicional construtivo :)
29er
Eu faria (p[i/n|0] || (p[i/n|0] = [])), então você não atribuir um valor, se não for necessário ...
yckart
12

Eu pretendia criar uma solução simples e não mutante no ES6 puro. As peculiaridades em javascript tornam necessário preencher a matriz vazia 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 versão com recursão parece mais simples e mais atraente:

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

As funções de matriz ridiculamente fracas do ES6 criam bons quebra-cabeças :-)

thoredge
fonte
1
Eu também escrevi o meu assim. Ele ainda funciona se você remover a 0partir da fill, o que torna a fillolhar um pouco mais sensato, imho.
Coert Grobbelaar
12

Eu acho que essa é uma boa solução recursiva com sintaxe 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
fonte
9

Criou um pacote 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;

Ao usar um TypedArray

var result = [];

for (var i = 0; i < arr.length; i += size) {
  result.push(arr.subarray(i, size + i));
}
return result;
zhiyelee
fonte
@ A1rPun Meu mal, eu não adicionei comentários lá. Sim, não há nenhum slicemétodo para TypedArray, podemos usar subarrayem vez developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/...
zhiyelee
8

Se você usa a versão EcmaScript> = 5.1, pode implementar uma versão funcional do chunk()uso de array.reduce () que possui complexidade 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' ] ]

Explicação de cada um // nbracima:

  1. Crie um novo pedaço se o valor anterior, ou seja, a matriz de pedaços retornada anteriormente, estiver vazio ou se o último pedaço anterior tiver chunkSizeitens
  2. Adicione o novo pedaço à matriz de pedaços existentes
  3. Caso contrário, o pedaço atual é o último pedaço da matriz de pedaços
  4. Adicione o valor atual ao pedaço
  5. Retornar a matriz modificada de pedaços
  6. Inicialize a redução passando uma matriz vazia

Caril com base em chunkSize:

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

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

Você pode adicionar a chunk()função ao 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
fonte
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
fonte
a.splice (0, 2) remove a sub-matriz de a [0..1] de a e retorna a sub-matriz a [0..1]. Eu estou fazendo uma matriz de todas essas matrizes
Arpit Jain
2
Eu recomendo usar o método slice não destrutivo () em vez de splice ()
Ash Azul
7
results = []
chunk_size = 10
while(array.length > 0){
   results.push(array.splice(0, chunk_size))
}
Jon
fonte
1
Não sei por que isso foi recusado, mas o código poderia usar alguma explicação.
jpaugh
2
Porque a emenda é destrutiva para a matriz original.
metalim
7

A seguinte abordagem do ES2015 funciona sem a necessidade de definir uma função e diretamente em matrizes anônimas (exemplo com tamanho de bloco 2):

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

Se você deseja definir uma função para isso, pode fazê-lo da seguinte forma (melhorando o comentário de K. _ na resposta do Blazemonger ):

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

E essa seria minha contribuição para esse tópico. Eu acho que .reduce()é o melhor caminho.

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

Mas a implementação acima não é muito eficiente, pois .reduce()executa todas as arrfunções. Uma abordagem mais eficiente (muito próxima da solução imperativa mais rápida) seria iterar sobre a matriz reduzida (a ser dividida em partes), pois podemos calcular seu tamanho antecipadamente Math.ceil(arr/n);. Uma vez que tenhamos a matriz de resultados vazia, Array(Math.ceil(arr.length/n)).fill();o restante será mapear fatias da arrmatriz para ele.

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
fonte
5

Usar pedaço de lodash

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

Mais uma solução 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 solução é muito semelhante à solução de Steve Holgado . No entanto, como essa solução não utiliza a dispersão de matriz e não cria novas matrizes na função redutora, é mais rápida (consulte o teste jsPerf ) e subjetivamente mais legível (sintaxe mais simples) do que a outra solução.

A cada enésima iteração (em que n = size; iniciando na primeira iteração), a matriz do acumulador ( acc) é anexada com uma parte da matriz ( arr.slice(i, i + size)) e depois retornada. Em outras iterações, a matriz do acumulador é retornada como está.

Se sizefor zero, o método retornará uma matriz vazia. Se sizefor negativo, o método retornará resultados quebrados. Portanto, se necessário no seu caso, convém fazer algo sobre valores negativos ou não positivos size.


Se a velocidade for importante no seu caso, um forloop simples seria mais rápido do que o uso arr.reduce()(consulte o teste jsPerf ), e alguns podem achar esse estilo mais legível também:

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
fonte
4

Para uma solução funcional, usando o Ramda :

Onde popularProductsestá sua matriz de entrada, 5é o tamanho do pedaço

import splitEvery from 'ramda/src/splitEvery'

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

})

Damian Green
fonte
4

Abordagem de linha única ES6 com base Array.prototype reducee 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
fonte
4

Versão do ES6 Generator

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
fonte
Use em constvez disso.
Константин Ван
4

Esta é a solução mais eficiente e direta que eu poderia pensar:

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
fonte
4

ES6 espalha 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
fonte
4

Aqui está uma solução recursiva que é a otimização da chamada final.

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
fonte
2

EDIT: @ mblase75 adicionou um código mais conciso à resposta anterior enquanto escrevia o meu, por isso recomendo ir com a solução dele.

Você pode 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));
}

Altere o valor de arraySizepara alterar o comprimento máximo das matrizes menores.

dentaku
fonte
2

Aqui está uma solução não mutante usando apenas recursão e fatia ().

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

Em seguida, basta usá-lo como splitToChunks([1, 2, 3, 4, 5], 3)para obter [[1, 2, 3], [4, 5]].

Aqui está um violino para você experimentar: https://jsfiddle.net/6wtrbx6k/2/

Suhair Zain
fonte