将数组拆分成块

假设我有一个如下所示的Javascript数组:

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

将数组分块(拆分)成许多更小的数组是合适的,可以说最多10个元素?

array.slice方法可以从数组的开始,中间或结束中提取一个片段,无论您需要什么目的, 都不需要更改原始数组。

 var i,j,temparray,chunk = 10; for (i=0,j=array.length; i<j; i+=chunk) { temparray = array.slice(i,i+chunk); // do whatever } 

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

演示:

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

次要附录

我应该指出,上述是一个不是那么优雅(在我心中)使用Array.map解决方法。 它基本上做了以下,其中〜是串联:

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

它具有与下面的方法相同的渐近运行时间,但是由于构build空列表,可能是一个更糟的常数因子。 我们可以把这个改写成如下(大多数情况和Blazemonger的方法一样,这就是为什么我最初没有提交这个答案):

更高效的方法:

 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 

现在我最喜欢的方式是上面的,或者下面的一种:

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

演示:

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

或者如果你不想要一个Array.range函数,它实际上只是一个单行(不包括绒毛):

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

要么

 Object.defineProperty(Array.prototype, 'chunk', {value: function(n) { return Array.from(Array(ceil(this.length/n)), (_,i)=>this.slice(i*n,i*n+n)); }}); 

尽量避免使用原生原型,包括Array.prototype,如果你不知道谁将会使用你的代码(第三方,同事,以后自己等等)。

有一些方法可以安全地扩展原型(但不是在所有的浏览器中),并且有方法可以安全地使用从扩展原型创build的对象,但更好的经验法则是遵循最小惊奇原则并完全避免这些实践。

如果您有一些时间,请观看Andrew Dupont的2011年JSConf会议“允许的一切:扩展内置” ,以获得有关此主题的良好讨论。

但是回到上面的问题,虽然上面的解决scheme可以工作,但它们太复杂,需要不必要的计算开销。 这是我的解决scheme:

 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; 

老问题:新的答案! 其实我正在从这个问题的答案,并有一个朋友改善它! 所以这里是:

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

我宁愿使用拼接方法:

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

这是一个使用reduce的ES6版本

 X = 2; // items per chunk a = ['a','b','c','d']; a.reduce((ar, it, i) => { const ix = Math.floor(i/X); if(!ar[ix]) { ar[ix] = []; } ar[ix].push(it); return ar; }, []) // result: [["a","b"], ["c","d"]] 

而且您已经准备好将其链接到进一步的地图/缩小转换。 您的input数组保持不变

我testing了jsperf.com的不同答案。 结果可以在这里find: http : //jsperf.com/chunk-mtds

而最快的function(从IE8的作品)是这样的:

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

现在,你可以使用lodash的chunk函数将数组分割成更小的数组https://lodash.com/docs#chunk不需要再循环了!;

好吧,让我们从一个相当紧密的开始:

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

这是这样使用的:

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

那么我们有这个紧缩机的function:

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

这是这样使用的:

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

由于一只小猫当我们将this绑定到一个数字上时就会死亡,我们可以这样做:

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

这是这样使用的:

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

然后仍然非常紧凑的function,它一气呵成:

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

如果使用EcmaScript版本> = 5.1,则可以使用具有O(N)复杂性的array.reduce chunk()来实现chunk()的function版本:

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

每个// nbr上面的解释:

  1. 如果之前的值(即以前返回的块的数组)为空,或者如果最后一个块有chunkSize项,则创build一个新的块
  2. 将新块添加到现有块的数组中
  3. 否则,当前块是块数组中的最后一个块
  4. 将当前值添加到块
  5. 返回修改后的数组块
  6. 通过传递一个空数组来初始化约简

基于chunkSize Currying:

 var chunk3 = function(array) { return chunk(3, array); }; console.log(chunk3(['a', 'b', 'c', 'd', 'e'])); // prints [ [ 'a', 'b', 'c' ], [ 'd', 'e' ] ] 

您可以将chunk()函数添加到全局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 ] ] 

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

为此https://www.npmjs.com/package/array.chunk创build一个npm包;

  var result = []; for (var i = 0; i < arr.length; i += size) { result.push(arr.slice(i, size + i)); } return result; 

这是我使用Coffeescript列表理解的方法。 在这里可以find一篇详细介绍Coffeescript的文章。

 chunk: (arr, size) -> chunks = (arr.slice(index, index+size) for item, index in arr by size) return chunks 
 results = [] chunk_size = 10 while(array.length > 0){ results.push(array.splice(0, chunk_size)) } 

编辑:@ mblase75添加更简洁的代码,以前的答案,而我写我的,所以我build议去他的解决scheme。

你可以使用这样的代码:

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

更改arraySize的值以更改较小数组的最大长度。

我稍微改变了BlazeMonger的用于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); } 

我创build了下面的JSFiddle来展示我的方法来解决你的问题。

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

我刚刚写了一个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)); 

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(var c of chunkArray(a,10)) console.log(c); 

这将是我对这个话题的贡献。 我猜.reduce()是最好的方法。

 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); console.log(segment(arr,7)); 

这里是chunk()函数的整洁和优化的实现。 假设默认块大小是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); 

我的目标是在纯ES6中创build一个简单的非变异解决scheme。 javascript中的特殊性使得在映射之前填充空数组是必要的:-(

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

这个recursion版本看起来更简单,更引人注目:

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

ES6的可笑的弱arraysfunction使好的难题:-)

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

那么,一个很好的function就是:

 function chunk(arr,times){ if(times===null){var times = 10} //Fallback for users wanting to use the default of ten var tempArray = Array() //Array to be populated with chunks for(i=0;i<arr.length/times;i++){ tempArray[i] = Array() //Sub-Arrays //Repeats for each chunk for(j=0;j<times;j++){ if(!(arr[i*times+j]===undefined)){tempArray[i][j] = arr[i*times+j]//Populate Sub- Arrays with chunks } else{ j = times //Stop loop i = arr.length/times //Stop loop } } } return tempArray //Return the populated and chunked array } 

用法是:

 chunk(array,sizeOfChunks) 

我评论说,所以你可以理解发生了什么事情。

(格式化是有点closures,我在手机上编程)

 Array.prototype.sliceIntoChunks = function(chunkSize) { var chunks = []; var temparray = null; for (var i = 0; i < this.length; i++) { if (i % chunkSize === 0) { temparray = new Array(); chunks.push(temparray); } temparray.push(this[i]); } return chunks; }; 

您可以使用如下:

 var myArray = ["A", "B", "C", "D", "E"]; var mySlicedArray = myArray.sliceIntoChunks(2); 

结果:

 mySlicedArray[0] = ["A", "B"]; mySlicedArray[1] = ["C", "D"]; mySlicedArray[2] = ["E"]; 

这里有一个使用ImmutableJS的解决scheme,其中items是不可变列表, size是必需的分组大小。

 const partition = ((items, size) => { return items.groupBy((items, i) => Math.floor(i/size)) }) 

尝试使用下面的代码片段

 const chunkSize = 5; var chunkArray = {}; var counter = 0; while(array.length > 0){ chunkArray[counter] = array.splice(0,chunkSize); counter++; } 

这是一个只使用recursion和slice()的非变异解决scheme。

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

然后简单地使用它像splitToChunks([1, 2, 3, 4, 5], 3)得到[[1, 2, 3], [4, 5]]

这里是一个小提琴你试试: https : //jsfiddle.net/6wtrbx6k/2/

她是一个使用@Blazemonger解决scheme的简单解决scheme

 function array_chunk(arr, size){ // initialize vars var i, j = arr.length, tempArray = []; // loop through and jump based on size for (i=0; i<j; i+=size) { // slice chunk of arr and push to tempArray tempArray.push(arr.slice(i,i+size)); } // return temp array (chunck) return tempArray } 

这得到了pipe道为我stream动,希望这有助于其他人在那里。 🙂