将数组分成块

1005

假设我有一个JavaScript数组,如下所示:

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

如何将一个数组分成多个小数组,每个小数组最多包含10个元素?


参见 如何将长数组拆分为较小的数组使用underscore.js拆分javascript数组 (以及链接问题中的许多重复项 linked questions)。 - Bergi
可能是将JS数组分成N个数组的重复问题。 - T J
70
对于lodash用户,您需要查找_.chunk - Ulysse BN
如果你需要最后一个块的最小大小,这里有几个选项:https://stackoverflow.com/questions/57908133/splitting-an-array-up-into-chunks-of-a-given-size-with-a-minimum-chunk-size - Ahmet Cetin
我创建了一个解决方案,合并了最佳答案:https://dev59.com/4Goy5IYBdhLWcg3wg-Ym#71483760 - Fernando Leal
@webatrisans 这回答了你的问题。如果你使用正确的标签,那么我们更容易给出正确的指针指向正确的重复内容。 - RiggsFolly
84个回答

1

我刚刚使用了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));


1

一种有效的解决方案是将 join 和 slicepush 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));


1
递归方式

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


1
这个问题可能有很多解决方案。我最喜欢的之一是:

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


1

我在这里找不到一个确保块大小相等的答案,尽管元素数量是奇数。因此,我编写了自己的方法来解决这个问题。这确保了块始终具有指定大小,填充的空洞为提供的默认值。这也不会修改原始数组。

现代版本:

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


1

好的,这是一种稍微增强版的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);
  }
}


1
这是一个使用尾递归和数组解构的版本。 远非最快,但我只是觉得现在js可以做到这一点很有趣。即使它并没有针对此进行优化 :(
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]]

1

嗨,试试这个 -

 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)

1
晚了一步,这是我的两分钱。和许多人一样,我最初会想到类似于的东西。
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))

解释

  1. 通常的方法是先确定块数,然后根据每个块的位置和大小获取原始数组的切片。

  2. 块缩减函数将遍历每个元素,并将其放入相应评估块的数组中。

性能几乎相同,就我所见,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 也可以用于创建命名数组(也称为对象),并返回字符串而不是数字的函数 :)

1

如果对任何人有用的话,这可以在 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]


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接