Diviser le tableau en morceaux

516

Disons que j'ai un tableau Javascript qui ressemble à ceci:

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

Quelle approche serait appropriée pour fragmenter (diviser) le tableau en plusieurs tableaux plus petits avec, disons, 10 éléments au maximum?

Industriel
la source
Duplication possible de la division d'un tableau JS en N tableaux
TJ
24
Pour les utilisateurs de lodash , vous recherchez _.chunk .
Ulysse BN
2
Je viens d'essayer const chunk = (arr, n) => arr.length ? [arr.slice(0, n), ...chunk(arr.slice(n), n)] : []ce qui est agréable et court mais semble prendre environ 256 × aussi longtemps que la réponse de @ AymKdn pour 1 000 éléments, et 1 058 × aussi longtemps pour 10 000 éléments!
Toph
si vous avez également besoin de la taille minimale du dernier morceau, voici les options: stackoverflow.com/questions/57908133/…
Ahmet Cetin

Réponses:

643

La méthode array.slice peut extraire une tranche du début, du milieu ou de la fin d'un tableau à toutes fins utiles, sans modifier le tableau d'origine.

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
la source
19
Rappelez-vous s'il s'agit d'une fonction utilitaire pour affirmer contre l' chunkêtre 0. (boucle infinie)
Steven Lu
19
Non, le dernier morceau doit être plus petit que les autres.
Blazemonger
7
@Blazemonger, en effet! La prochaine fois, je vais l'essayer moi-même avant de tirer des conclusions. J'ai supposé (à tort) que passer une entrée dans array.slice qui dépassait les limites du tableau serait un problème, mais cela fonctionne parfaitement!
rysqui
55
Pour une seule ligne (chaîne amateurs) : 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
Pourquoi avez-vous besoin de j? J'ai d'abord pensé que c'était une optimisation, mais elle est en fait plus lente que pour (i = 0; i <array.length; i ++) {}
Alex
149

Modifié à partir d'une réponse 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]]


addendum mineur :

Je dois souligner que ce qui précède est une solution de contournement pas si élégante (dans mon esprit) à utiliser Array.map. Il fait essentiellement ce qui suit, où ~ est la concaténation:

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

Il a le même temps d'exécution asymptotique que la méthode ci-dessous, mais peut-être un facteur constant pire en raison de la création de listes vides. On pourrait réécrire ceci comme suit (principalement la même que la méthode de Blazemonger, c'est pourquoi je n'ai pas soumis cette réponse à l'origine):

Méthode plus efficace:

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


De nos jours, ma voie préférée est celle ci-dessus, ou l'une des suivantes:

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

  }
});

Démo:

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

Ou si vous ne voulez pas d'une fonction Array.range, c'est en fait juste une ligne (à l'exception du fluff):

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
la source
41
Eh, j'éviterais de jouer avec le prototype car la sensation de fraîcheur que vous obtenez en appelant la chunkfonction sur le tableau ne l'emporte pas vraiment sur la complexité supplémentaire que vous ajoutez et les bugs subtils que peut causer le fait de jouer avec des prototypes intégrés.
Gordon Gustafson
12
Il ne joue pas avec eux, il les étend pour les tableaux. Je comprends de ne jamais toucher Object.prototype parce que cela bouillonnerait à tous les objets (tout) mais pour cette fonction spécifique à Array, je ne vois aucun problème.
rlemon
À peu près sûr que cela devrait être array.map(function(i)pas array.map(function(elem,i)si
Nigel Ange
3
Basé sur le tableau de compatibilité sur le site de développement mozilla, Array.map for for IE9 +. Faites attention.
Maikel D
Faites attention à passer flottant - 7,5 types de nombres - conduisez à des morceaux imprévisibles
Gal Bracha
103

Voici une version ES6 utilisant réduire

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

Et vous êtes prêt à enchaîner davantage la carte / réduire les transformations. Votre tableau d'entrée est laissé intact


Si vous préférez une version plus courte mais moins lisible, vous pouvez en saupoudrer concatdans le mix pour le même résultat final:

inputArray.reduce((all,one,i) => {
   const ch = Math.floor(i/perChunk); 
   all[ch] = [].concat((all[ch]||[]),one); 
   return all
}, [])
Andrei R
la source
Cela semble être la solution la plus condensée. Que reçoit chunkIndex = Math.floor (index / perChunk)? Est-ce la moyenne?
me-me
5/2 = 2.5et Math.floor(2.5) = 2donc l'élément avec l'index 5 sera placé dans le seau 2
Andrei R
Merci Andrei R. Ah donc ça passe par 0 - 2. Alors comment ça s'appelle en termes mathématiques? Je suppose que mon point est que je n'aurais jamais pensé à diviser index / 2 chaque index pour obtenir l'indice de chaque tranche. J'essaie donc d'envelopper ma tête parce que je l'aime vraiment mais je ne le comprends pas complètement en termes mathématiques. Je fais généralement cela pour obtenir des moyennes d'un nombre total, mais cela semble différent.
me-me
Cette solution est inefficace par rapport à d'autres solutions car vous devez parcourir chaque élément.
JP de la Torre
vous avez raison @JPdelaTorre, ce n'est probablement pas aussi efficace que les solutions qui découpent les tableaux, mais vous divisez les cheveux ici. la plupart des réponses énumérées seraient inefficaces selon cette définition.
Andrei R
102

Essayez d'éviter le bourrage avec des prototypes natifs, y compris Array.prototype, si vous ne savez pas qui va consommer votre code (tiers, collègues, vous-même à une date ultérieure, etc.).

Il existe des moyens d'étendre les prototypes en toute sécurité (mais pas dans tous les navigateurs) et il existe des moyens de consommer en toute sécurité des objets créés à partir de prototypes étendus, mais une meilleure règle de base est de suivre le principe de la moindre surprise et d'éviter complètement ces pratiques.

Si vous avez un peu de temps, regardez la conférence JSConf 2011 d'Andrew Dupont, "Tout est permis: extension des fonctions intégrées" , pour une bonne discussion sur ce sujet.

Mais revenons à la question, bien que les solutions ci-dessus fonctionnent, elles sont trop complexes et nécessitent des frais de calcul inutiles. Voici ma solution:

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
la source
25
"Évitez de faire le tourbillon avec des prototypes natifs" Les nouveaux développeurs de js devraient se faire tatouer temporairement, sinon définitivement, cette phrase.
Jacob Dalton
2
J'utilise javascript depuis des années et je ne passe presque pas de temps à me soucier du prototype, tout au plus des fonctions d'appel, sans jamais modifier comme vous le voyez certaines personnes.
1984
2
la meilleure suggestion à mes yeux, la plus simple à comprendre et à mettre en œuvre, merci beaucoup!
Olga Farber
1
@JacobDalton Je pense que c'est la faute de toutes les universités. Les gens pensent que la POO doit être utilisée partout. Ils ont donc peur de "créer simplement une fonction". Ils veulent être sûrs de le mettre dans quelque chose. Même si ce n'est pas du tout approprié. S'il n'y a pas de notation par points, il n'y a pas d '"architecture".
Gherman
51

J'ai testé les différentes réponses sur jsperf.com. Le résultat est disponible ici: https://web.archive.org/web/20150909134228/https://jsperf.com/chunk-mtds

Et la fonction la plus rapide (et qui fonctionne à partir d'IE8) est celle-ci:

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
la source
1
Merci @AymKdn d'avoir fait cette référence: cela a été très utile! J'utilisais l' approche par épissure et il a planté mon navigateur Chrome v70 à une taille de bloc de 884432. Avec votre approche de "tranche" suggérée en place, mon code ne plante plus le processus de "rendu" du navigateur. :)
Benny Neugebauer
Voici une version dactylographiée de ceci: 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 Il y a
34

Je préfère utiliser la méthode d' épissure :

var chunks = function(array, size) {
  var results = [];
  while (array.length) {
    results.push(array.splice(0, size));
  }
  return results;
};
Arek Flinik
la source
16
Doit être prudent avec cette solution d'épissage car elle modifie le tableau d'origine, alors assurez-vous de documenter clairement les effets secondaires.
bdrx
3
Utilisez alors slice à la place
mplungjan
@mplungjan Le résultat serait le même tableau encore et encore lors de l'utilisation de la tranche. Ce n'est donc pas vraiment un remplacement sans changement supplémentaire.
nietonfir
La seule chose que j'ajouterais à cette réponse est un clone du tableau d'origine. Je le ferais avec l'opérateur de propagation d'ES6. var clone = [...array]puis effectuez la vérification de la longueur et l'épissage sur ce tableau cloné.
MauricioLeal
1
Ou si vous ne pouvez pas utiliser les fonctionnalités ES6, vous pouvez simplement array = array.slice()créer une copie superficielle.
3limin4t0r
34

Une doublure dans 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
la source
9
il modifie le listtableau d' origine
Jacka
6
Correction facile en utilisant .slice () .. .map((_,i) => list.slice(i*chuckSize,i*chuckSize+chuckSize))
James Robey
2
Sur JSPerf, cela est considérablement plus performant que la plupart des autres réponses.
Micah Henning du
30

Ancienne question: nouvelle réponse! En fait, je travaillais avec une réponse à cette question et j'ai demandé à un ami de l'améliorer! Voici donc:

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
la source
14
fyi, les performances de cette méthode sont O (N ^ 2), elle ne doit donc pas être utilisée dans les sections de code critiques pour les performances, ou avec de longs tableaux (en particulier, lorsque le tableau .lengthest beaucoup plus grand que la taille de bloc n). S'il s'agissait d'un langage paresseux (contrairement au javascript), cet algorithme ne souffrirait pas du temps O (N ^ 2). Cela dit, l'implémentation récursive est élégante. Vous pouvez probablement le modifier pour améliorer les performances en définissant d'abord une fonction d'assistance qui se répète array,position, puis en répartissant : Array.prototype.chunkrenvoie [votre fonction d'assistance] (...)
ninjagecko
merci sensai de m'avoir béni de ton esprit que j'ai dû chanceler ce soir
mordu
1
ou ...var chunk = (arr, n) => { if ( !arr.length ) return []; return [ arr.slice( 0, n ) ].concat( chunk(arr.slice(n), n) ) }
Ed Williams
28

De nos jours, vous pouvez utiliser la fonction chunk de lodash pour diviser le tableau en plus petits tableaux https://lodash.com/docs#chunk Plus besoin de jouer avec les boucles!

George Herolyants
la source
3
Je pense qu'il devrait y avoir une exclusion de responsabilité pour les questions SO javascript: avez-vous essayé lodash? À peu près la première chose que j'inclus dans le nœud ou le navigateur.
Jacob Dalton
20

Il y a eu beaucoup de réponses mais voici ce que j'utilise:

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

Tout d'abord, recherchez un reste lorsque vous divisez l'index par la taille du bloc.

S'il y a un reste, renvoyez simplement le tableau d'accumulateurs.

S'il n'y a pas de reste, alors l'index est divisible par la taille du morceau, alors prenez une tranche du tableau d'origine (en commençant à l'index actuel) et ajoutez-la au tableau d'accumulateurs.

Ainsi, le tableau d'accumulateurs renvoyé pour chaque itération de réduction ressemble à ceci:

// 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
la source
Belle solution et belle représentation visuelle des itérations. Je me suis retrouvé avec une solution très similaire que j'ai publiée comme réponse: stackoverflow.com/a/60779547
mts knn
15

Utilisation de générateurs

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
la source
3
J'ai été surpris de toutes les réponses à cette question qui utilisaient presque des générateurs ou les utilisaient de manière plus compliquée. Belle concision et performances avec cette solution.
Keith
14

Ok, commençons par un assez serré:

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

Qui est utilisé comme ceci:

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

Ensuite, nous avons cette fonction de réduction serrée:

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

Qui est utilisé comme ceci:

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

Puisqu'un chaton meurt quand nous nous lions this à un nombre, nous pouvons effectuer un curry manuel comme ceci à la place:

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

Qui est utilisé comme ceci:

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

Ensuite, la fonction encore assez serrée qui fait tout en une seule fois:

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
la source
Ne fonctionne pas dans iE8.
Nadeemmnn Mohd du
4
HA! j'aime le commentaire de chaton. désolé pour aucune contribution constructive supplémentaire :)
29er
Je le ferais (p[i/n|0] || (p[i/n|0] = [])), donc vous n'attribuez pas de valeur, si ce n'est pas nécessaire ...
yckart
12

Je visais à créer une solution simple non mutante en ES6 pur. Les particularités en javascript obligent à remplir le tableau vide avant le mappage :-(

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

Cette version avec récursivité semble plus simple et plus convaincante:

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

Les fonctions de tableau ridiculement faibles d'ES6 font de bons puzzles :-)

thoredge
la source
1
J'ai aussi écrit le mien un peu comme ça. Cela fonctionne toujours si vous supprimez le 0du fill, ce qui rend le filllook un peu plus sensible, à mon humble avis.
Coert Grobbelaar
12

Je pense que c'est une belle solution récursive avec la syntaxe 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
la source
9

Création d'un package npm pour ce 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;

Lors de l'utilisation d'un TypedArray

var result = [];

for (var i = 0; i < arr.length; i += size) {
  result.push(arr.subarray(i, size + i));
}
return result;
zhiyelee
la source
@ A1rPun Mon mauvais, je n'ai pas ajouté de commentaire là-bas. Oui, il n'y a pas de sliceméthode pour TypedArray, nous pouvons utiliser à la subarrayplace developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
zhiyelee
8

Si vous utilisez la version EcmaScript> = 5.1, vous pouvez implémenter une version fonctionnelle de l' chunk()utilisation de array.reduce () qui a une complexité 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' ] ]

Explication de chacun // nbrci-dessus:

  1. Créez un nouveau bloc si la valeur précédente, c'est-à-dire le tableau de blocs précédemment renvoyé, est vide ou si le dernier bloc précédent contient des chunkSizeéléments
  2. Ajouter le nouveau morceau à la gamme de morceaux existants
  3. Sinon, le bloc actuel est le dernier bloc du tableau de blocs
  4. Ajouter la valeur actuelle au morceau
  5. Renvoyer le tableau modifié de morceaux
  6. Initialiser la réduction en passant un tableau vide

Curry basé sur chunkSize:

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

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

Vous pouvez ajouter la chunk()fonction à l' Arrayobjet 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
la source
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
la source
a.splice (0, 2) supprime le sous-tableau de [0..1] de a et renvoie le sous-tableau a [0..1]. Je fais un tableau de tous ces tableaux
Arpit Jain
2
Je recommande d'utiliser la méthode slice () non destructive au lieu de splice ()
Ash Blue
7
results = []
chunk_size = 10
while(array.length > 0){
   results.push(array.splice(0, chunk_size))
}
Jon
la source
1
Je ne sais pas pourquoi cela a été rejeté, mais le code pourrait utiliser une explication.
jpaugh
2
Parce que l'épissure est destructrice pour le tableau d'origine.
metalim
7

L'approche ES2015 suivante fonctionne sans avoir à définir de fonction et directement sur des tableaux anonymes (exemple avec la taille de bloc 2):

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

Si vous voulez définir une fonction pour cela, vous pouvez le faire comme suit (en améliorant le commentaire de K. sur la réponse 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)
user1460043
la source
6

Et ce serait ma contribution à ce sujet. Je suppose que .reduce()c'est la meilleure façon.

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

Mais l'implémentation ci-dessus n'est pas très efficace car elle .reduce()parcourt toutes les arrfonctions. Une approche plus efficace (très proche de la solution impérative la plus rapide) serait d'itérer sur le tableau réduit (à fragmenter) car nous pouvons calculer sa taille à l'avance par Math.ceil(arr/n);. Une fois que nous avons le tableau de résultats vide comme Array(Math.ceil(arr.length/n)).fill();le reste, il faut y mapper des tranches arr.

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
la source
5

Utilisez un morceau de lodash

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

Encore une solution en utilisant 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)

Cette solution est très similaire à la solution de Steve Holgado . Cependant, parce que cette solution n'utilise pas l'étalement des tableaux et ne crée pas de nouveaux tableaux dans la fonction de réduction , c'est plus rapide (voir test jsPerf ) et subjectivement plus lisible (syntaxe plus simple) que l'autre solution.

À chaque nième itération (où n = size; à partir de la première itération), le tableau accumulateur ( acc) est ajouté avec un morceau du tableau (arr.slice(i, i + size) ) puis renvoyé. À d'autres itérations, le tableau d'accumulateurs est renvoyé tel quel.

Si sizeest égal à zéro, la méthode renvoie un tableau vide. Si sizeest négatif, la méthode renvoie des résultats cassés. Donc, si nécessaire dans votre cas, vous voudrez peut-être faire quelque chose à propos des sizevaleurs négatives ou non positives .


Si la vitesse est importante dans votre cas, une simple forboucle serait plus rapide que l'utilisation arr.reduce()(voir le test jsPerf ), et certains trouveront peut-être aussi ce style plus lisible:

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
la source
4

Pour une solution fonctionnelle, en utilisant Ramda :

popularProductsest votre tableau d'entrée, 5est la taille du bloc

import splitEvery from 'ramda/src/splitEvery'

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

})

Damian Green
la source
4

Approche unifilaire ES6 basée sur Array.prototype reduceet pushméthodes:

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
la source
4

ES6 Générateur Version

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
la source
Utilisez constplutôt.
Константин Ван
4

C'est la solution la plus efficace et la plus simple à laquelle je puisse penser:

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
la source
4

ES6 propage #ohmy #ftw fonctionnel

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
la source
4

Voici une solution récursive qui est l'optimisation des appels de queue.

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
la source
2

EDIT: @ mblase75 a ajouté un code plus concis à la réponse précédente pendant que j'écrivais la mienne, donc je recommande d'aller avec sa solution.

Vous pouvez utiliser du code comme celui-ci:

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

Modifiez la valeur de arraySizepour modifier la longueur maximale des petits tableaux.

dentaku
la source
2

Voici une solution non mutante utilisant uniquement la récursivité et slice ().

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

Ensuite, utilisez-le simplement comme splitToChunks([1, 2, 3, 4, 5], 3)pour obtenir [[1, 2, 3], [4, 5]].

Voici un violon à essayer: https://jsfiddle.net/6wtrbx6k/2/

Suhair Zain
la source