将JS数组拆分为N个数组。

时间:2022-06-16 12:43:00

Imagine I have an JS array like this:

假设我有一个这样的JS数组:

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

What I want is to split that array into N smaller arrays. For instance:

我想要把这个数组分成N个小数组。例如:

split_list_in_n(a, 2)
[[1, 2, 3, 4, 5, 6], [7, 8, 9, 10, 11]]

For N = 3:
[[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11]]

For N = 4:
[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11]]

For N = 5:
[[1, 2, 3], [4, 5], [6, 7], [8, 9], [10, 11]]

For Python, I have this:

对于Python,我有一个:

def split_list_in_n(l, cols):
    """ Split up a list in n lists evenly size chuncks """
    start = 0
    for i in xrange(cols):
        stop = start + len(l[i::cols])
        yield l[start:stop]
        start = stop

For JS, the best right solution that I could come up with is a recursive function, but I don't like it because it's complicated and ugly. This inner function returns an array like this [1, 2, 3, null, 4, 5, 6, null, 7, 8], and then I have to loop it again and split it manually. (My first attempt was returning this: [1, 2, 3, [4, 5, 6, [7, 8, 9]]], and I decided to do it with the null separator).

对于JS,我能想到的最好的解决方案是递归函数,但我不喜欢它,因为它既复杂又丑陋。这个内部函数返回一个数组,像这样[1,2,3,空,4,5,6,空,7,8],然后我必须再次循环,并手动分割它。(我的第一次尝试是返回这个:[1,2,3,[4,5,6,[7,8,9]]),我决定用null分隔符来做。

function split(array, cols) {
    if (cols==1) return array;
    var size = Math.ceil(array.length / cols);
    return array.slice(0, size).concat([null]).concat(split(array.slice(size), cols-1));
}

Here's a jsfiddle of that: http://jsfiddle.net/uduhH/

这里有一个jsfiddle: http://jsfiddle.net/uduhH/。

How would you do that? Thanks!

你会怎么做?谢谢!

16 个解决方案

#1


106  

You can make the slices "balanced" (subarrays' lengths differ as less as possible) or "even" (all subarrays but the last have the same length):

您可以使片“平衡”(子数组的长度尽可能地不同)或“甚至”(所有子数组,但最后的长度相同):

function chunkify(a, n, balanced) {
    
    if (n < 2)
        return [a];

    var len = a.length,
            out = [],
            i = 0,
            size;

    if (len % n === 0) {
        size = Math.floor(len / n);
        while (i < len) {
            out.push(a.slice(i, i += size));
        }
    }

    else if (balanced) {
        while (i < len) {
            size = Math.ceil((len - i) / n--);
            out.push(a.slice(i, i += size));
        }
    }

    else {

        n--;
        size = Math.floor(len / n);
        if (len % size === 0)
            size--;
        while (i < size * n) {
            out.push(a.slice(i, i += size));
        }
        out.push(a.slice(size * n));

    }

    return out;
}


///////////////////////

onload = function () {
    function $(x) {
        return document.getElementById(x);
    }

    function calc() {
        var s = +$('s').value, a = [];
        while (s--)
            a.unshift(s);
        var n = +$('n').value;
        $('b').textContent = JSON.stringify(chunkify(a, n, true))
        $('e').textContent = JSON.stringify(chunkify(a, n, false))
    }

    $('s').addEventListener('input', calc);
    $('n').addEventListener('input', calc);
    calc();
}
<p>slice <input type="number" value="20" id="s"> items into
<input type="number" value="6" id="n"> chunks:</p>
<pre id="b"></pre>
<pre id="e"></pre>

#2


6  

I just made an iterative implementation of the algorithm: http://jsfiddle.net/ht22q/. It passes your test cases.

我只是做了一个迭代的算法实现:http://jsfiddle.net/ht22q/。它通过了您的测试用例。

function splitUp(arr, n) {
    var rest = arr.length % n, // how much to divide
        restUsed = rest, // to keep track of the division over the elements
        partLength = Math.floor(arr.length / n),
        result = [];

    for(var i = 0; i < arr.length; i += partLength) {
        var end = partLength + i,
            add = false;

        if(rest !== 0 && restUsed) { // should add one element for the division
            end++;
            restUsed--; // we've used one division element now
            add = true;
        }

        result.push(arr.slice(i, end)); // part of the array

        if(add) {
            i++; // also increment i in the case we added an extra element for division
        }
    }

    return result;
}

#3


6  

function split(arr, n) {
  var res = [];
  while (arr.length) {
    res.push(arr.splice(0, n));
  }
  return res;
}

#4


6  

You can reduce it into a matrix. The example below split the array (arr) into a matrix of two-positions arrays. If you want other sizes just change the 2 value on the second line:

你可以把它化成一个矩阵。下面的示例将数组(arr)拆分为两个位置数组的矩阵。如果你想要其他尺寸,请在第二行上更改两个值:

target.reduce((memo, value, index) => {
  if (index % 2 == 0 && index !== 0) memo.push([])
  memo[memo.length - 1].push(value)
  return memo
}, [[]])

Hope it helps!

希望它可以帮助!

EDIT: Because some people is still commenting this doesn't answer the question since I was fixing the size of each chunk instead of the number of chunks I want. Here it comes the code explaining what I'm trying to explain in the comments section: Using the target.length.

编辑:因为有些人还在评论这个问题,所以我没有回答这个问题,因为我在修改每个块的大小,而不是我想要的块的数量。在这里,代码解释了我在注释部分中试图解释的内容:使用target.length。

// Chunk function

const chunk = (target, size) => {
  return target.reduce((memo, value, index) => {
    // Here it comes the only difference
    if (index % (target.length / size) == 0 && index !== 0) memo.push([])
    memo[memo.length - 1].push(value)
    return memo
  }, [[]])
}

// Usage

write(chunk([1, 2, 3, 4], 2))
write(chunk([1, 2, 3, 4], 4))

// For rendering pruposes. Ignore
function write (content) { document.write(JSON.stringify(content), '</br>') }

#5


3  

Old question, but since vanillaJS is not a requirement and so many are trying to solve this with lodash/chunk, and without mistaking what _.chunk actually does, here's a concise + accurate solution using lodash:

老问题,但是因为vanillaJS不是一个要求,所以很多人尝试用lodash/chunk来解决这个问题,而且没有错误的定义。实际上,这是一个简洁的、精确的使用lodash的解决方案:

(Unlike the accepted answer, this also guarantees n columns even if originalArray.length < numCols)

(与公认的答案不同,这也保证了n列,即使是原始数组。长度< numCols)

import _chunk from 'lodash/chunk'

/**
 * Split an array into n subarrays (or columns)
 * @param  {Array} flatArray Doesn't necessarily have to be flat, but this func only works 1 level deep
 * @param  {Number} numCols   The desired number of columns
 * @return {Array}
 */
export function splitArray(flatArray, numCols){
  const maxColLength = Math.ceil(flatArray.length/numCols)
  const nestedArray = _chunk(flatArray, maxColLength)
  let newArray = []
  for (var i = 0; i < numCols; i++) {
    newArray[i] = nestedArray[i] || []
  }
  return newArray
}

The for loop at the end is what guarantees the desired number of "columns".

最后的for循环是保证所期望的“列”数量的方法。

#6


2  

Recursive approach, not tested.

递归的方法,而不是测试。

function splitArray(array, parts, out) {
    var
        len = array.length
        , partLen

    if (parts < len) {
        partLen = Math.ceil(len / parts);
        out.push(array.slice(0, partLen));
        if (parts > 1) {
            splitArray(array.slice(partLen), parts - 1, out);
        }
    } else {
        out.push(array);
    }
}

#7


2  

Another recursive works quite well, it is less ugly

另一个递归的工作很好,它不那么难看。

function nSmaller(num, arr, sliced) {

    var mySliced = sliced || [];
    if(num === 0) {
        return sliced;
    }

    var len = arr.length,
        point = Math.ceil(len/num),
        nextArr = arr.slice(point);

    mySliced.push(arr.slice(0, point));
    nSmaller(num-1, nextArr, mySliced);

    return(mySliced);
}

#8


1  

if you know wanna set child_arrays.length then i think this solution best:

如果你知道要设置child_array。长度,我认为这个解决方案最好:

function sp(size, arr){ //size - child_array.length
    var out = [],i = 0, n= Math.ceil((arr.length)/size); 
    while(i < n) { out.push(arr.splice(0, (i==n-1) && size < arr.length ? arr.length: size));  i++;} 
    return out;
}

call fn: sp(2, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]) //2 - child_arrat.length

调用fn: sp(2, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]) //2 - child_arrat.length。

answer: [1, 2], [3, 4], [5, 6], [7, 8], [9, 10], [11]

答:[1,2],[3,4],[5,6],[7,8],[9 10],[11]

#9


1  

Probably the cleaner approach would be the following (without using any other library) :

更清洁的方法可能是以下(不使用任何其他库):

var myArray = [];
for(var i=0; i<100; i++){
  myArray.push(i+1);
}
console.log(myArray);

function chunk(arr, size){
  var chunkedArr = [];
  var noOfChunks = Math.ceil(arr.length/size);
  console.log(noOfChunks);
  for(var i=0; i<noOfChunks; i++){
    chunkedArr.push(arr.slice(i*size, (i+1)*size));
  }
   return chunkedArr;
}

var chunkedArr = chunk(myArray, 3);
console.log(chunkedArr);

I have created my own array which is to be chunked. You can find the code here

我已经创建了自己的数组,它将被分块。你可以在这里找到代码。

Also we have a method "chunk" in the lodash library which is of great use. Hope that helps

此外,我们还在lodash库中有一个非常有用的方法“chunk”。希望这有助于

#10


1  

function splitArray(arr, numOfParts = 10){
        const splitedArray = []
        for (let i = 0; i < numOfParts;i++) {
            const numOfItemsToSplice = arr.length / 10;
            splitedArray.push(arr.splice(0, numOfItemsToSplice))
        }
        return splitedArray;
    }

#11


0  

I made it this way, it works...

我这样做了,它起作用了……

function splitArray(array, parts) {
    if (parts< array.length && array.length > 1 && array != null) {
        var newArray = [];
        var counter1 = 0;
        var counter2 = 0;

        while (counter1 < parts) {
            newArray.push([]);
            counter1 += 1;
        }

        for (var i = 0; i < array.length; i++) {
            newArray[counter2++].push(array[i]);
            if (counter2 > parts - 1)
                counter2 = 0;
        }

        return newArray;
    } else 
        return array;
}

#12


0  

check my version of this array split

检查我的版本的这个数组拆分。

// divide array
Array.prototype.divideIt = function(d){
    if(this.length <= d) return this;
    var arr = this,
        hold = [],
        ref = -1;
    for(var i = 0; i < arr.length; i++){
        if(i % d === 0){
            ref++;
        }
        if(typeof hold[ref] === 'undefined'){
            hold[ref] = [];
        }
        hold[ref].push(arr[i]);
    }

    return hold;
};

#13


0  

Just use lodash' chunk function to split the array into smaller arrays https://lodash.com/docs#chunk No need to fiddle with the loops anymore!

只需使用lodash' chunk函数将数组拆分为更小的数组https://lodash.com/docs#块,不再需要拨动这些循环了!

#14


0  

If you can use lodash and would like a functional programming approach, here is what I come up with:

如果您可以使用lodash并希望使用函数式编程方法,下面是我的建议:

const _ = require('lodash')

function splitArray(array, numChunks) {
  return _.reduce(_.range(numChunks), ({array, result, numChunks}, chunkIndex) => {
    const numItems = Math.ceil(array.length / numChunks)
    const items = _.take(array, numItems)
    result.push(items)
    return {
      array: _.drop(array, numItems),
      result,
      numChunks: numChunks - 1
    }
  }, {
    array,
    result: [],
    numChunks
  }).result
} 

#15


0  

all above might work fine, but what if you have associative array with strings as keys?

上面的所有操作都可以正常工作,但是如果将字符串作为键进行关联,会怎么样呢?

objectKeys = Object.keys;

arraySplit(arr, n) {
    let counter = 0;
    for (const a of this.objectKeys(arr)) {
        this.arr[(counter%n)][a] = arr[a];
        counter++;
    }
}

#16


-3  

If you are using lodash, you can achieve it fairly easily like below:

如果您使用的是lodash,您可以很容易地做到如下所示:

import {chunk} from 'lodash';
// divides the array into 2 sections
chunk([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], 2); // => [[1,2,3,4,5,6], [7,8,9,10,11]]

#1


106  

You can make the slices "balanced" (subarrays' lengths differ as less as possible) or "even" (all subarrays but the last have the same length):

您可以使片“平衡”(子数组的长度尽可能地不同)或“甚至”(所有子数组,但最后的长度相同):

function chunkify(a, n, balanced) {
    
    if (n < 2)
        return [a];

    var len = a.length,
            out = [],
            i = 0,
            size;

    if (len % n === 0) {
        size = Math.floor(len / n);
        while (i < len) {
            out.push(a.slice(i, i += size));
        }
    }

    else if (balanced) {
        while (i < len) {
            size = Math.ceil((len - i) / n--);
            out.push(a.slice(i, i += size));
        }
    }

    else {

        n--;
        size = Math.floor(len / n);
        if (len % size === 0)
            size--;
        while (i < size * n) {
            out.push(a.slice(i, i += size));
        }
        out.push(a.slice(size * n));

    }

    return out;
}


///////////////////////

onload = function () {
    function $(x) {
        return document.getElementById(x);
    }

    function calc() {
        var s = +$('s').value, a = [];
        while (s--)
            a.unshift(s);
        var n = +$('n').value;
        $('b').textContent = JSON.stringify(chunkify(a, n, true))
        $('e').textContent = JSON.stringify(chunkify(a, n, false))
    }

    $('s').addEventListener('input', calc);
    $('n').addEventListener('input', calc);
    calc();
}
<p>slice <input type="number" value="20" id="s"> items into
<input type="number" value="6" id="n"> chunks:</p>
<pre id="b"></pre>
<pre id="e"></pre>

#2


6  

I just made an iterative implementation of the algorithm: http://jsfiddle.net/ht22q/. It passes your test cases.

我只是做了一个迭代的算法实现:http://jsfiddle.net/ht22q/。它通过了您的测试用例。

function splitUp(arr, n) {
    var rest = arr.length % n, // how much to divide
        restUsed = rest, // to keep track of the division over the elements
        partLength = Math.floor(arr.length / n),
        result = [];

    for(var i = 0; i < arr.length; i += partLength) {
        var end = partLength + i,
            add = false;

        if(rest !== 0 && restUsed) { // should add one element for the division
            end++;
            restUsed--; // we've used one division element now
            add = true;
        }

        result.push(arr.slice(i, end)); // part of the array

        if(add) {
            i++; // also increment i in the case we added an extra element for division
        }
    }

    return result;
}

#3


6  

function split(arr, n) {
  var res = [];
  while (arr.length) {
    res.push(arr.splice(0, n));
  }
  return res;
}

#4


6  

You can reduce it into a matrix. The example below split the array (arr) into a matrix of two-positions arrays. If you want other sizes just change the 2 value on the second line:

你可以把它化成一个矩阵。下面的示例将数组(arr)拆分为两个位置数组的矩阵。如果你想要其他尺寸,请在第二行上更改两个值:

target.reduce((memo, value, index) => {
  if (index % 2 == 0 && index !== 0) memo.push([])
  memo[memo.length - 1].push(value)
  return memo
}, [[]])

Hope it helps!

希望它可以帮助!

EDIT: Because some people is still commenting this doesn't answer the question since I was fixing the size of each chunk instead of the number of chunks I want. Here it comes the code explaining what I'm trying to explain in the comments section: Using the target.length.

编辑:因为有些人还在评论这个问题,所以我没有回答这个问题,因为我在修改每个块的大小,而不是我想要的块的数量。在这里,代码解释了我在注释部分中试图解释的内容:使用target.length。

// Chunk function

const chunk = (target, size) => {
  return target.reduce((memo, value, index) => {
    // Here it comes the only difference
    if (index % (target.length / size) == 0 && index !== 0) memo.push([])
    memo[memo.length - 1].push(value)
    return memo
  }, [[]])
}

// Usage

write(chunk([1, 2, 3, 4], 2))
write(chunk([1, 2, 3, 4], 4))

// For rendering pruposes. Ignore
function write (content) { document.write(JSON.stringify(content), '</br>') }

#5


3  

Old question, but since vanillaJS is not a requirement and so many are trying to solve this with lodash/chunk, and without mistaking what _.chunk actually does, here's a concise + accurate solution using lodash:

老问题,但是因为vanillaJS不是一个要求,所以很多人尝试用lodash/chunk来解决这个问题,而且没有错误的定义。实际上,这是一个简洁的、精确的使用lodash的解决方案:

(Unlike the accepted answer, this also guarantees n columns even if originalArray.length < numCols)

(与公认的答案不同,这也保证了n列,即使是原始数组。长度< numCols)

import _chunk from 'lodash/chunk'

/**
 * Split an array into n subarrays (or columns)
 * @param  {Array} flatArray Doesn't necessarily have to be flat, but this func only works 1 level deep
 * @param  {Number} numCols   The desired number of columns
 * @return {Array}
 */
export function splitArray(flatArray, numCols){
  const maxColLength = Math.ceil(flatArray.length/numCols)
  const nestedArray = _chunk(flatArray, maxColLength)
  let newArray = []
  for (var i = 0; i < numCols; i++) {
    newArray[i] = nestedArray[i] || []
  }
  return newArray
}

The for loop at the end is what guarantees the desired number of "columns".

最后的for循环是保证所期望的“列”数量的方法。

#6


2  

Recursive approach, not tested.

递归的方法,而不是测试。

function splitArray(array, parts, out) {
    var
        len = array.length
        , partLen

    if (parts < len) {
        partLen = Math.ceil(len / parts);
        out.push(array.slice(0, partLen));
        if (parts > 1) {
            splitArray(array.slice(partLen), parts - 1, out);
        }
    } else {
        out.push(array);
    }
}

#7


2  

Another recursive works quite well, it is less ugly

另一个递归的工作很好,它不那么难看。

function nSmaller(num, arr, sliced) {

    var mySliced = sliced || [];
    if(num === 0) {
        return sliced;
    }

    var len = arr.length,
        point = Math.ceil(len/num),
        nextArr = arr.slice(point);

    mySliced.push(arr.slice(0, point));
    nSmaller(num-1, nextArr, mySliced);

    return(mySliced);
}

#8


1  

if you know wanna set child_arrays.length then i think this solution best:

如果你知道要设置child_array。长度,我认为这个解决方案最好:

function sp(size, arr){ //size - child_array.length
    var out = [],i = 0, n= Math.ceil((arr.length)/size); 
    while(i < n) { out.push(arr.splice(0, (i==n-1) && size < arr.length ? arr.length: size));  i++;} 
    return out;
}

call fn: sp(2, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]) //2 - child_arrat.length

调用fn: sp(2, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]) //2 - child_arrat.length。

answer: [1, 2], [3, 4], [5, 6], [7, 8], [9, 10], [11]

答:[1,2],[3,4],[5,6],[7,8],[9 10],[11]

#9


1  

Probably the cleaner approach would be the following (without using any other library) :

更清洁的方法可能是以下(不使用任何其他库):

var myArray = [];
for(var i=0; i<100; i++){
  myArray.push(i+1);
}
console.log(myArray);

function chunk(arr, size){
  var chunkedArr = [];
  var noOfChunks = Math.ceil(arr.length/size);
  console.log(noOfChunks);
  for(var i=0; i<noOfChunks; i++){
    chunkedArr.push(arr.slice(i*size, (i+1)*size));
  }
   return chunkedArr;
}

var chunkedArr = chunk(myArray, 3);
console.log(chunkedArr);

I have created my own array which is to be chunked. You can find the code here

我已经创建了自己的数组,它将被分块。你可以在这里找到代码。

Also we have a method "chunk" in the lodash library which is of great use. Hope that helps

此外,我们还在lodash库中有一个非常有用的方法“chunk”。希望这有助于

#10


1  

function splitArray(arr, numOfParts = 10){
        const splitedArray = []
        for (let i = 0; i < numOfParts;i++) {
            const numOfItemsToSplice = arr.length / 10;
            splitedArray.push(arr.splice(0, numOfItemsToSplice))
        }
        return splitedArray;
    }

#11


0  

I made it this way, it works...

我这样做了,它起作用了……

function splitArray(array, parts) {
    if (parts< array.length && array.length > 1 && array != null) {
        var newArray = [];
        var counter1 = 0;
        var counter2 = 0;

        while (counter1 < parts) {
            newArray.push([]);
            counter1 += 1;
        }

        for (var i = 0; i < array.length; i++) {
            newArray[counter2++].push(array[i]);
            if (counter2 > parts - 1)
                counter2 = 0;
        }

        return newArray;
    } else 
        return array;
}

#12


0  

check my version of this array split

检查我的版本的这个数组拆分。

// divide array
Array.prototype.divideIt = function(d){
    if(this.length <= d) return this;
    var arr = this,
        hold = [],
        ref = -1;
    for(var i = 0; i < arr.length; i++){
        if(i % d === 0){
            ref++;
        }
        if(typeof hold[ref] === 'undefined'){
            hold[ref] = [];
        }
        hold[ref].push(arr[i]);
    }

    return hold;
};

#13


0  

Just use lodash' chunk function to split the array into smaller arrays https://lodash.com/docs#chunk No need to fiddle with the loops anymore!

只需使用lodash' chunk函数将数组拆分为更小的数组https://lodash.com/docs#块,不再需要拨动这些循环了!

#14


0  

If you can use lodash and would like a functional programming approach, here is what I come up with:

如果您可以使用lodash并希望使用函数式编程方法,下面是我的建议:

const _ = require('lodash')

function splitArray(array, numChunks) {
  return _.reduce(_.range(numChunks), ({array, result, numChunks}, chunkIndex) => {
    const numItems = Math.ceil(array.length / numChunks)
    const items = _.take(array, numItems)
    result.push(items)
    return {
      array: _.drop(array, numItems),
      result,
      numChunks: numChunks - 1
    }
  }, {
    array,
    result: [],
    numChunks
  }).result
} 

#15


0  

all above might work fine, but what if you have associative array with strings as keys?

上面的所有操作都可以正常工作,但是如果将字符串作为键进行关联,会怎么样呢?

objectKeys = Object.keys;

arraySplit(arr, n) {
    let counter = 0;
    for (const a of this.objectKeys(arr)) {
        this.arr[(counter%n)][a] = arr[a];
        counter++;
    }
}

#16


-3  

If you are using lodash, you can achieve it fairly easily like below:

如果您使用的是lodash,您可以很容易地做到如下所示:

import {chunk} from 'lodash';
// divides the array into 2 sections
chunk([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], 2); // => [[1,2,3,4,5,6], [7,8,9,10,11]]