Fendre le tableau en morceaux
disons que j'ai un tableau Javascript ressemblant à ceci:
["Element 1","Element 2","Element 3",...]; // with close to a hundred elements.
quelle approche serait appropriée pour fractionner le tableau en plusieurs tableaux plus petits avec, disons, 10 éléments au maximum?
30 réponses
le tableau .la méthode slice peut extraire une tranche du début, du milieu, ou de la fin d'un tableau pour n'importe quelles fins que vous exigez, sans changer le tableau 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
}
modifié 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)];
})
);
}
});
Démo:
> [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 n'est pas une solution élégante (dans mon esprit) d'utiliser Array.map
. Il fait essentiellement ce qui suit, où ~ est concaténation:
[[1,2,3]]~[]~[]~[] ~ [[4,5,6]]~[]~[]~[] ~ [[7]]
il a le même temps de fonctionnement asymptotique que la méthode ci-dessous, mais peut-être un facteur constant pire en raison de la construction de listes vides. On pourrait réécrire ceci comme suit (la plupart du temps la même que la méthode de Blazemonger, qui est pourquoi je n'ai pas soumis à l'origine cette réponse):
méthode plus efficace:
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
ma méthode préférée de nos jours est la suivante:
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 de tableau.fonction d'autonomie, il s'agit en fait d'une doublure unique (à l'exclusion de la doublure):
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));
}});
essayer d'éviter de salir 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 y a des moyens d'étendre les prototypes en toute sécurité (mais pas dans tous les navigateurs) et il y a des moyens de consommer en toute sécurité les objets créés à partir de prototypes étendus, mais une meilleure règle empirique est de suivre le principe de la moindre Surprise et d'éviter ces pratiques tout à fait.
si vous avez un peu de temps, regardez Andrew Dupont jsconf 2011 parler, " tout est permis: extension construit-ins " , pour une bonne discussion sur ce sujet.
mais revenons à la question, bien que les solutions ci-dessus fonctionneront, elles sont trop complexes et exigent des frais généraux 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;
Voici une version ES6 utilisant reduce
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']]
et vous êtes prêt à enchaîner d'autres transformations. Votre tableau d'entrées est laissé intact
si vous préférez une version plus courte mais moins lisible, vous pouvez saupoudrer un peu de concat
dans le mélange 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
}, [])
je préférerais utiliser splice method:
var chunks = function(array, size) {
var results = [];
while (array.length) {
results.push(array.splice(0, size));
}
return results;
};
ancienne question: Nouvelle réponse! En fait je travaille avec une réponse de cette question et avait un ami pour l'améliorer! Alors voilà:
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]]
j'ai testé les différentes réponses en jsperf.com. Le résultat est disponible ici: http://jsperf.com/chunk-mtds
et la fonction la plus rapide (et qui fonctionne à partir de 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;
}
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!
One-liner in ECMA 6
const [list,chuckSize] = [[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15], 6]
new Array(Math.ceil(list.length / chuckSize)).fill().map(_ => list.splice(0,chuckSize))
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);
alors nous avons cette fonction réducteur serré:
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),[]);
depuis qu'un chaton meurt quand nous lions this
à un nombre, nous pouvons faire manuel currying 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),[]);
puis la fonction encore assez étroite qui fait tout d'un coup:
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);
j'ai voulu créer une solution simple non mutante dans ES6 pur. Les particularités de javascript rendent nécessaire de remplir le tableau vide avant la mise en correspondance: - (
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écursion semblent plus simple et plus convaincant:
function chunk(a, l) {
if (a.length == 0) return [];
else return [a.slice(0, l)].concat(chunk(a.slice(l), l));
}
les fonctions de tableaux ridiculement faibles de ES6 fait de bons puzzles: -)
si vous utilisez la version EcmaScript >= 5.1, vous pouvez implémenter une version fonctionnelle de chunk()
en utilisant le tableau .réduire() qui a O(N) la complexité:
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 chaque // nbr
ci-dessus:
- créer un nouveau morceau si la valeur précédente, c'est-à-dire le tableau de morceaux retournés précédemment, est vide ou si le dernier morceau précédent a
chunkSize
items - ajouter le nouveau morceau de l'éventail de morceaux existants
- sinon, le morceau courant est le dernier morceau dans la rangée de morceaux
- ajouter la valeur courante au morceau
- Retour le tableau modifié des morceaux de
- initialiser la réduction en passant par un tableau vide
Currying 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 fonction chunk()
à l'objet global Array
:
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' ] ]
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 ] ]
Il y a eu beaucoup de réponses, mais c'est 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, vérifiez s'il y a un reste lorsque vous divisez l'indice par la taille du morceau.
S'il y a un reste, retournez simplement le réseau d'accumulateurs.
S'il n'y a pas de reste, alors l'index est divisible par la taille du morceau, donc prenez une tranche du tableau original (en commençant par l'index courant) et ajoutez-la au tableau de l'accumulateur.
ainsi, le tableau d'accumulateurs retourné pour chaque itération de reduce ressemble à quelque chose comme ceci:
// 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]]
a créé un paquet 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;
results = []
chunk_size = 10
while(array.length > 0){
results.push(array.splice(0, chunk_size))
}
ES6 une approche basée sur Array.prototype
reduce
et push
mé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]]
Et ce serait ma contribution à ce sujet. Je suppose que .reduce()
est le meilleur moyen.
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 la mise en œuvre ci-dessus n'est pas très efficace puisque .reduce()
passe par toute la fonction arr
. Une approche plus efficace (très proche de la solution impérative la plus rapide) serait d'itérer sur le tableau réduit (à découper) puisque nous pouvons calculer sa taille à l'avance par Math.ceil(arr/n);
. Une fois que nous avons l' le tableau de résultat vide comme Array(Math.ceil(arr.length/n)).fill();
le reste est de mapper des tranches du tableau arr
dans lui.
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}).map((_,i) => i+1);
res = chunk(arr,7);
console.log(JSON.stringify(res));
Voici une solution non mutante qui n'utilise que la récursion et slice().
const splitToChunks = (arr, chunkSize, acc = []) => (
arr.length > chunkSize ?
splitToChunks(
arr.slice(chunkSize),
chunkSize,
[...acc, arr.slice(0, chunkSize)]
) :
[...acc, arr]
);
alors il suffit de l'utiliser comme splitToChunks([1, 2, 3, 4, 5], 3)
pour obtenir [[1, 2, 3], [4, 5]]
.
voici un violon que vous pouvez essayer: https://jsfiddle.net/6wtrbx6k/2 /
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);
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;
}
édite: @mblase75 a ajouté du 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 le code comme ceci:
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));
}
Change la valeur de arraySize
pour changer la longueur maximale des tableaux plus petits.
j'ai changé légèrement le Blazemonger's pour l'utiliser pour un objet 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);
}
j'ai créé le suivant JSFiddle pour démontrer mon approche à votre question.
(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;
}
}
tout d'abord, j'ai deux exemples: un tableau avec moins de huit éléments, un autre avec un tableau avec plus de huit éléments (commentez celui que vous ne voulez pas utiliser).
je vérifie ensuite la taille du tableau, simple mais essentiel pour évitez les calculs supplémentaires. De là, si le tableau répond aux critères (Taille du tableau > delimiter
), nous passons à la fonction splitArray
.
la fonction splitArray
prend le délimiteur (qui signifie 8, puisque c'est ce que vous voulez séparer), et le tableau lui-même. Puisque nous réutilisons beaucoup la longueur du tableau, je le cache dans une variable, ainsi que les first
et last
.
first
représente la position de la première élément dans un tableau. Ce tableau est un tableau de 8 éléments. Ainsi, pour déterminer le premier élément, nous utilisons le " module opérateur .
myArrays
est le tableau des tableaux. Dans celui-ci nous stockerons à chaque index, n'importe quel sous-tableau de la taille 8 ou moins. C'est la stratégie clé dans l'algorithme ci-dessous.
index
représente l'indice de la variable myArrays
. Chaque fois qu'un sous-tableau de 8 éléments, ou moins, est d'être stockée, il doit être stocké dans l'index correspondant. Donc si nous avons 27 éléments, cela signifie 4 tableaux. Le premier, Le deuxième et le troisième tableau auront 8 éléments chacun. Le dernier ne comportera que 3 éléments. Donc index
sera 0, 1, 2, et 3 respectivement.
la partie délicate consiste simplement à calculer les mathématiques et à les optimiser au mieux. Par exemple else if (delimiter - (i % delimiter) === 1)
c'est pour trouver le dernier élément que devrait aller dans le tableau, lorsqu'un tableau sera complet (exemple: contient 10 éléments).
ce code fonctionne pour chaque scénario, vous pouvez même changer le delimiter
pour correspondre à n'importe quelle taille de tableau que vous souhaitez obtenir. Assez doux à droite :-)
des questions? N'hésitez pas à demander dans les commentaires ci-dessous.
je viens d'écrire ceci avec l'aide d'une fonction 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));
Voici mon approche en utilisant la compréhension de la liste Coffeescript. Un grand article détaillant des interprétations en Coffee-script peut être trouvé ici .
chunk: (arr, size) ->
chunks = (arr.slice(index, index+size) for item, index in arr by size)
return chunks
Voici la mise en œuvre soignée et optimisée de la fonction chunk()
. En supposant que la taille de morceau par défaut est 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);
cela devrait être réponse simple sans beaucoup de complications mathématiques.
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;
}
Hi essayer ce - que
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)
# 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 ' '