如何使用 JavaScript 将长数组拆分为更小的数组

我有一个电子邮件数组(可以是1封电子邮件,也可以是100封电子邮件) ,我需要发送一个 ajax 请求数组(我知道如何做) ,但我只能发送一个包含10封或更少电子邮件的数组。因此,如果有一个包含20封电子邮件的原始数组,我需要将它们分成两个每个包含10封电子邮件的数组。或者如果原始数组中有15封电子邮件,那么1个数组为10,另一个数组为5。我在使用 jQuery,最好的方法是什么?

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

splice()不同,slice()对原始数组是非破坏性的。

只要循环遍历数组,拼接它,直到它全部消耗。




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


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

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
}

不要使用 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,因为你想要的。

作为@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];

你可以使用 loash: Https://lodash.com/docs

_.chunk(['a', 'b', 'c', 'd'], 2);
// → [['a', 'b'], ['c', 'd']]

你可以看看这段代码。简单而有效。

function chunkArrayInGroups(array, unit) {
var results = [],
length = Math.ceil(array.length / unit);


for (var i = 0; i < length; i++) {
results.push(array.slice(i * unit, (i + 1) * unit));
}
return results;
}


chunkArrayInGroups(["a", "b", "c", "d"], 2);

如果您想要一个不修改现有数组的方法,请尝试这样做:

let oldArray = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];
let newArray = [];
let size = 3; // Size of chunks you are after
let j = 0; // This helps us keep track of the child arrays


for (var i = 0; i < oldArray.length; i++) {
if (i % size === 0) {
j++
}
if(!newArray[j]) newArray[j] = [];
newArray[j].push(oldArray[i])
}

另一项实施措施:

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

注意: 这不会修改原始数组。

或者,如果你更喜欢功能性的,100% 不可变的(尽管像上面所做的那样在适当的位置进行变异并没有什么坏处)和自包含的方法:

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

另一种方法:

var longArray = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
var size = 2;


var newArray = new Array(Math.ceil(longArray.length / size)).fill("")
.map(function() { return this.splice(0, size) }, longArray.slice());


// newArray = [[1, 2], [3, 4], [5, 6], [7, 8], [9, 10]];

这不会影响原始数组,因为使用切片创建的副本被传递到 map 的“ This”参数中。

另一个实现,使用 Array.reduce (我认为这是唯一缺少的一个!) :

const splitArray = (arr, size) =>
{
if (size === 0) {
return [];
}


return arr.reduce((split, element, index) => {
index % size === 0 ? split.push([element]) : split[Math.floor(index / size)].push(element);
return split;
}, []);
};

正如上面的许多解决方案一样,这个方案是非破坏性的。当大小为0时返回空数组只是一种约定。如果省略了 if块,就会得到一个错误,这可能正是您想要的。

下面是一个简单的一行程序

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

我也想分享一下我的解决方案,虽然有点冗长,但是也很有效。

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    ] ]
function chunkArrayInGroups(arr, size) {
var newArr=[];


for (var i=0; arr.length>size; i++){
newArr.push(arr.splice(0,size));
}
newArr.push(arr.slice(0));
return newArr;


}


chunkArrayInGroups([0, 1, 2, 3, 4, 5, 6], 3);

Reduce 对于大型数组可能效率低下,特别是使用 mod 操作符时。我认为一个更简洁(也可能更容易理解)的功能性解决方案应该是这样的:

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

更紧凑:

const chunk = (xs, size) =>
xs.map((_, i) =>
(i % size === 0 ? xs.slice(i, i + size) : null)).filter(Boolean);
    

// Usage:
const sampleArray = new Array(33).fill(undefined).map((_, i) => i);


console.log(chunk(sampleArray, 5));

function chunkArrayInGroups(arr, size) {
var newArr=[];


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


}


chunkArrayInGroups([0, 1, 2, 3, 4, 5, 6], 3);

作为一种功能

var arrayChunk = function (array, chunkSize) {
var arrayOfArrays = [];


if (array.length <= chunkSize) {
arrayOfArrays.push(array);
} else {
for (var i=0; i<array.length; i+=chunkSize) {
arrayOfArrays.push(array.slice(i,i+chunkSize));
}
}
return arrayOfArrays;
}

使用

arrayChunk(originalArray, 10) //10 being the chunk size.

使用递归

let myArr = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16];
let size = 4; //Math.sqrt(myArr.length); --> For a n x n matrix
let tempArr = [];
function createMatrix(arr, i) {
if (arr.length !== 0) {
if(i % size == 0) {
tempArr.push(arr.splice(0,size))
}
createMatrix(arr, i - 1)
}
}
createMatrix(myArr, myArr.length);
console.log(tempArr);

注意: 现有的数组即 myArr 将被修改。

使用原型我们可以直接设置为数组类

Array.prototype.chunk = function(n) {
if (!this.length) {
return [];
}
return [this.slice(0, n)].concat(this.slice(n).chunk(n));
};
console.log([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15].chunk(5));

您可以从一个空数组开始,在从原始数组减去原始数组直到为空的同时,使用所需的范围推入其中的各个部分。

const originalArr = [1,2,3,4,5,6,7,8,9,10,11];
const splittedArray = [];
while (originalArr.length > 0) {
splittedArray.push(originalArr.splice(0,range));
}

输出范围3

splittedArray === [[1,2,3][4,5,6][7,8,9][10,11]]

输出范围4

splittedArray === [[1,2,3,4][5,6,7,8][9,10,11]]

如果需要的话,这也是一个很好的前置分页。

let original = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14];
let size = 5;
let fragments = Array.from(Array(Math.ceil(a.length / size))).map((_,index) => a.slice(index * size,(index + 1) * size))

您可以使用下面的代码来实现所需的功能

const splitter = (arr, splitBy, cache = []) => {
const tmp = [...arr]
while (tmp.length) cache.push(tmp.splice(0, splitBy))
return cache
}
const split = splitter([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21], 10)
console.log(split); 

注意,它是一个长度为22的数组,然后 分裂者函数将其分成两个较小的数组,分别包含10个项和1个包含2个项的数组。

let a = [1, 2, 3, 4, 6, 7, 8, 9, 10, 11];
let _splitCount = 3;


let b = a.reduce((acc, curr, index) => {
if (index % _splitCount === 0) {
acc.push([curr]);
} else {
acc[acc.length - 1].push(curr);
}
return acc;
}, []);

这是一个简单的解决方案,我认为弃

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

Make it work for infinite Generators

Using the same idea you can create a generator which can also generate an infinite amount of n-sized chunks from values retrieved from another (possibly infinite) generator function. This can be very handy to lazy-generate values once they are required which significantly reduces the required memory or it can even be used to generate a possibly infinite/ unknown number of chunks.

Here an example which uses two generators.

  • nextNaturalNumber() is an infinite generator which always returns the next natural number. I am using the ES2020 bigint datatype here so there is no restriction (by JavaScript) for the size of the value.
  • chunksFromIterable() creates n-sized chunks from an possibly infinite iterable.

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

如果知道要拆分的数组(NumGroup) ,可以使用下面的函数。

function createGroups(arr, numGroups) {
const perGroup = Math.ceil(arr.length / numGroups);
return new Array(numGroups)
.fill('')
.map((_, i) => arr.slice(i * perGroup, (i + 1) * perGroup));
}

使用范例:

createGroups([0, 1, 2, 3, 4, 5, 6], 3); //arr = [0, 1, 2, 3, 4, 5, 6] and numGroups = 3