如何使用JavaScript将一个长数组拆分成较小的数组

151

我有一个邮件数组(它可以只包含1封电子邮件,也可以包含100封电子邮件),我需要用ajax请求发送该数组(我知道如何做到这一点),但我只能发送包含10个或更少电子邮件的数组。因此,如果原始数组中有20封电子邮件,则需要将它们拆分为两个包含10封电子邮件的数组;如果原始数组中有15封电子邮件,则需要1个包含10封电子邮件和另一个包含5封电子邮件的数组。我正在使用jQuery,怎样才能最好地实现这一操作?

27个回答

268

不要使用 jQuery……使用普通的 JavaScript

var a = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];

var b = a.splice(0,10);

//a is now [11,12,13,14,15];
//b is now [1,2,3,4,5,6,7,8,9,10];
你可以循环执行此操作以获得你想要的行为。
var a = YOUR_ARRAY;
while(a.length) {
    console.log(a.splice(0,10));
}

这将使您每次获取10个元素......如果您有15个元素,您将得到1-10,然后是11-15,就像您想要的那样。


10
请注意,YOUR_ARRAY 也将被消耗掉(数组是对象...)。 - Claudio
是的,我只是这样做是因为a没有描述性,而且我已经使用a编写了示例...这是完全不必要的代码行...不过,这让我想到...如果使用这种模式,您可能希望将其深度复制到工作数组中,以避免破坏原始数据。 - jyore
@junvar 我不确定这个一行代码在2018年是否有效(我非常怀疑它没有),但是今天它会失败得很惨。您绝对不应该改变(即删除元素)正在迭代的数组,因为这会扰乱索引,即每次删除后,索引应通过删除的元素数量进行重新调整,以便“跳过”并且不完全消耗数组。试试吧 - Drew Reese

167
var size = 10; var arrayOfArrays = [];
for (var i=0; i<bigarray.length; i+=size) {
     arrayOfArrays.push(bigarray.slice(i,i+size));
}
console.log(arrayOfArrays);

slice()splice()不同,它不会对原始数组进行破坏。


4
这个解决方案更好。 - Chukwuemeka Maduekwe
@Enrico 不,这是正确的。 - Unmitigated

53

52

只需循环遍历数组,对其进行切割直到全部消耗完。



var a = ['a','b','c','d','e','f','g']
  , chunk

while (a.length > 0) {

  chunk = a.splice(0,3)

  console.log(chunk)

}

输出


[ 'a', 'b', 'c' ]
[ 'd', 'e', 'f' ]
[ 'g' ]


45

对于大型数组,尤其是涉及取模运算时,Array.reduce 可能效率较低。我认为更简洁(而且可能更易读)的函数解决方案如下:

const chunkArray = (arr, size) =>
  arr.length > size
    ? [arr.slice(0, size), ...chunkArray(arr.slice(size), size)]
    : [arr];

1
迄今为止最好、最正确的答案。 - inux

16

假设您不想破坏原始数组,您可以使用以下代码将长数组分解为较小的数组,然后对其进行迭代:

var longArray = [];   // assume this has 100 or more email addresses in it
var shortArrays = [], i, len;

for (i = 0, len = longArray.length; i < len; i += 10) {
    shortArrays.push(longArray.slice(i, i + 10));
}

// now you can iterate over shortArrays which is an 
// array of arrays where each array has 10 or fewer 
// of the original email addresses in it

for (i = 0, len = shortArrays.length; i < len; i++) {
    // shortArrays[i] is an array of email addresss of 10 or less
}

8

另一种实现方法:

const arr = ["H", "o", "w", " ", "t", "o", " ", "s", "p", "l", "i", "t", " ", "a", " ", "l", "o", "n", "g", " ", "a", "r", "r", "a", "y", " ", "i", "n", "t", "o", " ", "s", "m", "a", "l", "l", "e", "r", " ", "a", "r", "r", "a", "y", "s", ",", " ", "w", "i", "t", "h", " ", "J", "a", "v", "a", "S", "c", "r", "i", "p", "t"];

const size = 3; 
const res = arr.reduce((acc, curr, i) => {
  if ( !(i % size)  ) {    // if index is 0 or can be divided by the `size`...
    acc.push(arr.slice(i, i + size));   // ..push a chunk of the original array to the accumulator
  }
  return acc;
}, []);

// => [["H", "o", "w"], [" ", "t", "o"], [" ", "s", "p"], ["l", "i", "t"], [" ", "a", " "], ["l", "o", "n"], ["g", " ", "a"], ["r", "r", "a"], ["y", " ", "i"], ["n", "t", "o"], [" ", "s", "m"], ["a", "l", "l"], ["e", "r", " "], ["a", "r", "r"], ["a", "y", "s"], [",", " ", "w"], ["i", "t", "h"], [" ", "J", "a"], ["v", "a", "S"], ["c", "r", "i"], ["p", "t"]]

NB-这不会修改原始数组。

或者,如果您更喜欢一个功能性的、100%不可变的(虽然在原地突变没有什么坏处,就像上面所做的那样)和自包含的方法:

function splitBy(size, list) {
  return list.reduce((acc, curr, i, self) => {
    if ( !(i % size)  ) {  
      return [
          ...acc,
          self.slice(i, i + size),
        ];
    }
    return acc;
  }, []);
}

7
作为 @jyore 的 答案 的补充,并且如果您仍然想保留原始数组:
var originalArray = [1,2,3,4,5,6,7,8];

var splitArray = function (arr, size) {

  var arr2 = arr.slice(0),
      arrays = [];

  while (arr2.length > 0) {
      arrays.push(arr2.splice(0, size));
  }

  return arrays;
}

splitArray(originalArray, 2);
// originalArray is still = [1,2,3,4,5,6,7,8];

4
我也想分享我的解决方案。虽然有点啰嗦,但同样有效。
var data = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];

var chunksize = 4;


var chunks = [];

data.forEach((item)=>{
  if(!chunks.length || chunks[chunks.length-1].length == chunksize)
  chunks.push([]);

  chunks[chunks.length-1].push(item);
});

console.log(chunks);

输出(格式化):

[ [ 1,  2,  3,  4],
  [ 5,  6,  7,  8],
  [ 9, 10, 11, 12],
  [13, 14, 15    ] ]

4

使用ES6生成器

虽然有点晚,但是ES6生成器提供了另一种很棒的实现方式。

/**
 * Returns chunks of size n.
 * @param {Array<any>} array any array
 * @param {number} n size of chunk 
 */
function* chunks(array, n){
  for(let i = 0; i < array.length; i += n) yield array.slice(i, i + n);
}

const result = [...chunks([1, 2, 3, 4, 5, 6, 7, 8 , 9, 10], 3)];
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

让它适用于无限生成器

使用相同的思路,您可以创建一个生成器,该生成器也可以从另一个(可能是无限的)生成器函数中检索的值生成无限数量的n-sized块。这对于在需要时懒惰地生成值非常有用,从而显着减少所需的内存,甚至可以用于生成可能无限/未知数量的块。

下面是一个使用两个生成器的示例。

  • nextNaturalNumber() 是一个无限生成器,它始终返回下一个自然数。我在这里使用 ES2020 的 bigint 数据类型,因此没有 JavaScript 对该值大小的限制。
  • chunksFromIterable() 从可能是无限的可迭代对象中创建n-sized块。

/**
 * Returns chunks of size n for a possibly infinite iterator.
 * n must be >= 1
 * @param {Iterable<any>} iterable any array
 * @param {number} n size of chunk for n >= 1
 */
 function* chunksFromIterable(iterable, n){
  let arr = [];
  let i = n;
  for (const value of iterable) {
    if(i <= 0) {
      // another chunk of size n is filled => return chunk
      yield arr;
      arr = []; // create new empty array
      i = n; 
    };
    arr.push(value);
    i--;
  }
  // in case the iterable is not infinite check if there are still values in the array and return them if necessary
  if(arr.length > 0) yield arr;
}

/**
 * Infinite iterator which always gets the next natural number.
 */
function* nextNaturalNumber(){
  let i = 0n;
  while(true) {
    i += 1n;
    yield i;
  }
}

console.log("Finite iterable:");
// this version can now be used using the for ... of loop
for(const threeNaturalNumbers of chunksFromIterable([1, 2, 3, 4, 5, 6, 7, 8 , 9, 10], 3)){
  console.log(threeNaturalNumbers);
}

console.log("Infinite iterable:");
// and it can also be used for this infinite generator
for(const threeNaturalNumbers of chunksFromIterable(nextNaturalNumber(), 3)){
  printBigIntArray(threeNaturalNumbers);
  if(threeNaturalNumbers[0] > 30) break; // end here to avoid an infinite loop
}

// helper function to print array of bigints as this does not seem to be working for snippets
function printBigIntArray(arr){
  console.log(`[${arr.join(", ")}]`);
}
.as-console-wrapper { max-height: 100% !important; top: 0; }


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