假设我有一个JavaScript数组,如下所示:
["Element 1","Element 2","Element 3",...]; // with close to a hundred elements.
如何将一个数组分成多个小数组,每个小数组最多包含10个元素?
假设我有一个JavaScript数组,如下所示:
["Element 1","Element 2","Element 3",...]; // with close to a hundred elements.
如何将一个数组分成多个小数组,每个小数组最多包含10个元素?
我刚刚使用了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));
一种有效的解决方案是将 join 和 slice 与 push by indexChunk 结合起来,将解决方案分成块:
function splitChunks(sourceArray, chunkSize) {
if(chunkSize <= 0)
throw "chunkSize must be greater than 0";
let result = [];
for (var i = 0; i < sourceArray.length; i += chunkSize) {
result[i / chunkSize] = sourceArray.slice(i, i + chunkSize);
}
return result;
}
let ar1 = [
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20
];
console.log("Split in chunks with 4 size", splitChunks(ar1, 4));
console.log("Split in chunks with 7 size", splitChunks(ar1, 7));
function chunk(array, chunk_size){
if(array.length == 0) return [];
else return [array.splice(0, chunk_size)].concat(chunk(array, chunk_size))
}
console.log(chunk([1,2,3,4,5,6,7,8],3))
function chunk(array, size) {
const chunked = [];
for (element of array){
let last = chunked[chunked.length - 1];
if(last && last.length != size){
last.push(element)
}else{
chunked.push([element])
}
}
return chunked;
}
function chunk1(array, size) {
const chunked = [];
let index = 0;
while(index < array.length){
chunked.push(array.slice(index,index+ size))
index += size;
}
return chunked;
}
console.log('chunk without slice:',chunk([1,2,3,4,5,5],2));
console.log('chunk with use of slice funtion',chunk1([1,2,3,4,5,6],2))
我在这里找不到一个确保块大小相等的答案,尽管元素数量是奇数。因此,我编写了自己的方法来解决这个问题。这确保了块始终具有指定大小,填充的空洞为提供的默认值。这也不会修改原始数组。
现代版本:
// Modern version
function chunkArray(a, s, d) {
const l = a.length;
let p = 0;
if (l !== 0) {
return a.reduce((a, c, i) => {
if ((i % s) === 0)
p = a.push([]);
let r = a[p - 1].push(c);
if ((i + 1) === l)
while (r < s)
r = a[p - 1].push(d);
return a;
}, []);
} else
return [...Array(s).fill(d)];
}
const add = (v, i) => v + (i + 1);
console.log('a.length = 7, s = 3, d = 0');
console.log(chunkArray([...Array(7).fill(0)].map(add), 3, 0));
console.log('');
console.log('a.length = 12, s = 2, d = 2');
console.log(chunkArray([...Array(12).fill(0)].map(add), 2, 2));
console.log('');
console.log('a.length = 10, s = 6, d = "ADDITIONAL"');
console.log(chunkArray([...Array(10).fill('ORIGINAL')].map(add), 6, 'ADDITIONAL'));
console.log('');
console.log('a.length = 20, s = 12, d = undefined');
console.log(chunkArray([...Array(20).fill(0)].map(add), 12, undefined));
console.log('');
console.log('a.length = 30, s = 4, d = null');
console.log(chunkArray([...Array(30).fill('TEST')].map(add), 4, null));
兼容IE10+版本:
// IE10+ compatible version
function addArray(a) {
return a.map(function(v, i) { return v + (i + 1); });
}
function createArray(s, d) {
var a = [];
for (var i = 0; i < s; i++)
a.push(d);
return a;
}
function chunkArray(a, s, d) {
var l = a.length, p = 0, r = 0;
if (l !== 0) {
return a.reduce(function(a, c, i) {
if ((i % s) === 0)
p = a.push([]);
r = a[p - 1].push(c);
if ((i + 1) === l)
while (r < s)
r = a[p - 1].push(d);
return a;
}, []);
} else
return createArray(s, d);
}
console.log('a.length = 7, s = 3, d = 0');
console.log(chunkArray(addArray(createArray(7, 0)), 3, 0));
console.log('');
console.log('a.length = 12, s = 2, d = 2');
console.log(chunkArray(addArray(createArray(12, 0)), 2, 2));
console.log('');
console.log('a.length = 10, s = 6, d = "ADDITIONAL"');
console.log(chunkArray(addArray(createArray(10, 'ORIGINAL')), 6, 'ADDITIONAL'));
console.log('');
console.log('a.length = 20, s = 12, d = undefined');
console.log(chunkArray(addArray(createArray(20, 0)), 12, undefined));
console.log('');
console.log('a.length = 30, s = 4, d = null');
console.log(chunkArray(addArray(createArray(30, 'TEST')), 4, null));
好的,这是一种稍微增强版的Ikechukwu Eze答案,使用了生成器。
它更新了,因此源不必是一个数组,而可以是任何可迭代对象。
使用生成器和可迭代对象的主要好处是它们可以处理更多类型的数据,例如字符串、DOM元素等,还可以节省内存使用量,并且当然可以重复使用代码。还可以使用自定义生成器进行链式操作。
例如..
function *chunkIterator(iterable, chunkSize) {
let i, iter = iterable[Symbol.iterator]();
function *nextChunk() {
for (let l = 0; l < chunkSize && !i.done; l++) {
yield i.value;
i = iter.next();
}
}
i = iter.next();
while (!i.done) yield [...nextChunk()];
}
const myArray = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const chunkedArray = [...chunkIterator(myArray, 3)];
console.log(JSON.stringify(chunkedArray));
for (const chunk of chunkIterator('hello world!.', 3))
console.log(chunk.join(''));
还可以使块成为可迭代对象,这意味着不需要创建任何数组。并且由于块可以是任何可迭代对象,我创建了一个简单的随机数生成器,而不是提供一个数组。
例子 ->
function *chunkIterator(iterable, chunkSize) {
let i, iter = iterable[Symbol.iterator]();
function *nextChunk() {
for (let l = 0; l < chunkSize && !i.done; l++) {
yield i.value;
i = iter.next();
}
}
i = iter.next();
while (!i.done) yield nextChunk();
}
function *rand() {
for (let l = 0; l < 10; l++)
yield `${l} = ${(Math.random()*1000) | 0}`;
}
for (const r of chunkIterator(rand(), 3)) {
console.log('---');
for (const c of r) {
console.log(c);
}
}
const getChunks = (arr, chunk_size, acc = []) => {
if (arr.length === 0) { return acc }
const [hd, tl] = [ arr.slice(0, chunk_size), arr.slice(chunk_size) ]
return getChunks(tl, chunk_size, acc.concat([hd]))
}
// USAGE
const my_arr = [1,2,3,4,5,6,7,8,9]
const chunks = getChunks(my_arr, 2)
console.log(chunks) // [[1,2],[3,4], [5,6], [7,8], [9]]
嗨,试试这个 -
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)
chunker = (a,n) => [...Array(Math.ceil(a.length/n))].map((v,i) => a.slice(i*n, (i+1)*n))
但是我更喜欢并且在这里没有看到的是:
chunker = (n) => (r,v,i) => (c = Math.floor(i/n), (r[c] = r[c] || []).push(v), r)
console.log(arr.reduce(chunker(3), []))
带有更长变体
chunker = (a, n) => a.reduce((r,v,i) => {
c = Math.floor(i/n); // which chunk it belongs to
(r[c] = r[c] || []).push(v)
return r
}, [])
console.log(chunker(arr, 3))
通常的方法是先确定块数,然后根据每个块的位置和大小获取原始数组的切片。
块缩减函数将遍历每个元素,并将其放入相应评估块的数组中。
性能几乎相同,就我所见,reduce方法平均慢了4%。
PS:缩减具有轻松更改分组标准的优点。在问题和示例中,标准是相邻单元格(并且映射使用切片进行)。但是,您可能希望以“周期”为例,使用mod(%运算符)或任何其他数学公式来执行此操作
重新阅读它使我看到公式也可以是一个参数,从而得出更一般的解决方案,并需要2个函数来实现答案:
splitter = (a, f) => a.reduce((r,v,i) => { // math formula and/or function
c = f(v, i) || 0; // custom formula, receiving each value and index
(r[c] = r[c] || []).push(v)
return r
}, [])
chunker = (a, n) => splitter(a, (v,i) => Math.floor(i/n))
console.log(chunker(arr, 3))
console.log(splitter(arr, (v,i) => v % 2)) // is it even or odd?
splitter
也可以用于创建命名数组(也称为对象),并返回字符串而不是数字的函数 :)如果对任何人有用的话,这可以在 RxJS 6 中非常简单地完成:
const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
from(arr).pipe(bufferCount(3)).subscribe(chunk => console.log(chunk));
输出:[1, 2, 3] [4, 5, 6] [7, 8, 9] [10, 11, 12] [13, 14, 15] [16]