Javascript 将一个 JS 数组拆分为 N 个数组

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8188548/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 05:01:22  来源:igfitidea点击:

Splitting a JS array into N arrays

javascriptarrays

提问by Tiago

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, null, 4, 5, 6, null, 7, 8],然后我必须再次循环它并手动拆分它。(我的第一次尝试是返回这个:[1, 2, 3, [4, 5, 6, [7, 8, 9]]],我决定用空分隔符来做)。

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!

你会怎么做?谢谢!

回答by georg

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>

回答by pimvdb

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

回答by bagbee

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

回答by Senthe

I think this way using splice is the cleanest:

我认为这种使用 splice 的方式是最干净的:

splitToChunks(array, parts) {
    let result = [];
    for (let i = parts; i > 0; i--) {
        result.push(array.splice(0, Math.ceil(array.length / i)));
    }
    return result;
}

For example, for parts = 3, you would take 1/3, then 1/2 of the remaining part, then the rest of the array. Math.ceilensures that in case of uneven number of elements they will go to the earliest chunks.

例如,对于parts = 3,您将取剩余部分的 1/3,然后是 1/2,然后是数组的其余部分。Math.ceil确保在元素数量奇数的情况下,它们将转到最早的块。

(Note: this destroys the initial array.)

(注意:这会破坏初始数组。)

回答by sospedra

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) 拆分为两个位置数组的矩阵。如果您想要其他尺寸,只需更改第二行的 2 值:

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 chunksI 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>') }

回答by Joao

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

老问题,但由于 vanillaJS 不是必需的,而且很多人都试图用 lodash/chunk 来解决这个问题,而且不会弄错_.chunk实际的作用,这里有一个简洁+准确的解决方案,使用lodash

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

(与接受的答案不同,即使originalArray.length< ,这也保证了 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 forloop at the end is what guarantees the desired number of "columns".

最后的for循环保证了所需的“列”数。

回答by Mcs

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

回答by 12000

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

回答by user1034533

If you happen to know the size of the chunks you want beforehand, there's a pretty elegant ES6 way of doing this:

如果你碰巧事先知道你想要的块的大小,那么 ES6 有一种非常优雅的方式来做到这一点:

const groupsOfFour = ([a,b,c,d, ...etc]) =>
  etc.length? [[a,b,c,d], ...groupsOfFour(etc)] : [[a,b,c,d]];
  
console.log(groupsOfFour([1,2,3,4,1,2,3,4,1,2,3,4]));

I find this notation pretty useful for, for example parsing RGBA out of a Uint8ClampedArray.

我发现这种表示法非常有用,例如从Uint8ClampedArray.

回答by Stefdelec

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