将数组拆分成块
假设我有一个如下所示的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
上面的解释:
- 如果之前的值(即以前返回的块的数组)为空,或者如果最后一个块有
chunkSize
项,则创build一个新的块 - 将新块添加到现有块的数组中
- 否则,当前块是块数组中的最后一个块
- 将当前值添加到块
- 返回修改后的数组块
- 通过传递一个空数组来初始化约简
基于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动,希望这有助于其他人在那里。 🙂