将JS数组拆分为N个数组

Tia*_*ago 70 javascript arrays

想象一下,我有一个像这样的JS数组:

var a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
Run Code Online (Sandbox Code Playgroud)

我想要的是将该数组拆分为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]]
Run Code Online (Sandbox Code Playgroud)

对于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
Run Code Online (Sandbox Code Playgroud)

对于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));
}
Run Code Online (Sandbox Code Playgroud)

这是一个jsfiddle:http://jsfiddle.net/uduhH/

你会怎么做?谢谢!

geo*_*org 123

您可以切片"平衡"(子阵长度不同尽可能少)或'偶’(所有子阵列,但最后具有相同的长度):

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();
}
Run Code Online (Sandbox Code Playgroud)
<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>
Run Code Online (Sandbox Code Playgroud)

  • 像魅力的工作..很好的解决方案 (3认同)

pim*_*vdb 11

我只是对算法进行了迭代实现: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;
}
Run Code Online (Sandbox Code Playgroud)


小智 9

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

  • 对于 n = 5 和 arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],这不会按预期工作。 (2认同)

Joa*_*oao 7

更新:7/21/2020

我几年前给出的答案仅在originalArray.length<=时才有效numCols。您也可以使用下面类似的函数,但这会创建一个与手头问题不太匹配的布局(水平排序而不是垂直排序)。又名:[1,2,3,4]-> [[1,4],[2],[3]]。我知道这可能仍然提供价值,所以我将把它留在这里,但我推荐Senthe 的答案

function splitArray(flatArray, numCols){
  const newArray = []
  for (let c = 0; c < numCols; c++) {
    newArray.push([])
  }
  for (let i = 0; i < flatArray.length; i++) {
    const mod = i % numCols
    newArray[mod].push(flatArray[i])
  }
  return newArray
}
Run Code Online (Sandbox Code Playgroud)

2017 年的原始答案:

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

(与接受的答案不同,即使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
}
Run Code Online (Sandbox Code Playgroud)

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


Sen*_*the 7

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

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

例如,对于parts = 3,您将占用1/3,然后是剩余部分的1/2,然后是阵列的其余部分.Math.ceil确保在元素数量不均匀的情况下,它们将进入最早的块.

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

  • 这个解决方案对我有用。只是一个建议。为了不破坏初始数组,请添加此行 `const copyArray = array.map(v =&gt; v);` 以创建数组的浅表副本。然后在函数的其余部分中操作复制的数组。 (2认同)

sos*_*dra 6

您可以将其简化为矩阵。下面的示例将数组(arr)拆分为两个位置的数组的矩阵。如果需要其他尺寸,只需在第二行更改2值:

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

希望能帮助到你!

编辑:因为有些人仍在评论这不能回答问题,因为我正在固定每个块大小,而不是我想要的块数。这里是代码,解释了我要在注释部分中解释的内容:使用target.length

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

  • 喜欢这个 !!!我已经重构为返回大小均匀的块`function splitArr(arr,n){return arr.reduce(function(a,i){if(a [a.length-1] .length&gt; = arr.length / n ){a.push([])} a [a.length-1] .push(i)返回a;},[[]])}` (3认同)
  • 绝对不能回答这个问题。 (3认同)
  • 哇,简洁明了的方法!爱它!做得好!:-) (2认同)

use*_*533 6

如果你碰巧事先知道你想要的块的大小,有一个非常优雅的 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]));
Run Code Online (Sandbox Code Playgroud)

我发现这个符号非常有用,例如从Uint8ClampedArray.


Nic*_*rey 5

一般来说,突变是一件坏事™。

这很好,干净,幂等。

function partition(list = [], n = 1) {
  const isPositiveInteger = Number.isSafeInteger(n) && n > 0;
  if (!isPositiveInteger) {
    throw new RangeError('n must be a positive integer');
  }

  const partitions = [];
  const partitionLength = Math.ceil(list.length / n);

  for (let i = 0; i < list.length; i += partitionLength) {
    const partition = list.slice(i, i+partitionLength);
    partitions.push( partition );
  }

  return partitions;
}
Run Code Online (Sandbox Code Playgroud)

[编辑添加]

这是调用者指定分区大小而不是要创建的分区数的另一种风格:

function partition(list = [], n = 1) {
  const isPositiveInteger = Number.isSafeInteger(n) && n > 0;
  if (!isPositiveInteger) {
    throw new RangeError('n must be a positive integer');
  }

  const partitions = [];

  for (let i = 0; i < list.length; i += n) {
    const partition = list.slice(i, i+n);
    partitions.push( partition );
  }

  return partitions;
}
Run Code Online (Sandbox Code Playgroud)

如果您希望它是“平衡的”,使得各个块的长度差异不超过 1,那么只需要一点数学知识。

要以这种方式将 M 事物分配到 N 个桶中,我们需要首先确定 M / N 的商 Q 和余数 R。

让 Q 表示基本分区长度。R 将始终小于 N,并且是需要跨所有分区分布的多余项目的数量。因此,前 R 个分区将包含 Q+1 个项目,其余分区将包含 Q 个项目。

例如,要将 100 个项目的列表划分为 8 个桶,我们得到:

M = 10 N = 8 Q = 12 R = 4

所以我们会得到:

  • 4 (R) 桶 Q+1 (13) 项,以及
  • 4 (NR) 桶 Q (12) 项

4 * 13 + 4 * 12 减少到 52+48,即 100。

这导致我们:

function partition(list = [], n = 1) {
  const isPositiveInteger = Number.isSafeInteger(n) && n > 0;
  if (!isPositiveInteger) {
    throw new RangeError('n must be a positive integer');
  }

  const q = Math.floor( list.length / n );
  const r = list.length % n;

  let i   ; // denotes the offset of the start of the slice
  let j   ; // denotes the zero-relative partition number
  let len ; // denotes the computed length of the slice

  const partitions = [];
  for ( i=0, j=0, len=0; i < list.length; i+=len, ++j ) {
    len = j < r ? q+1 : q ;
    const partition = list.slice( i, i+len ) ; 
    partitions.push( partition ) ;
  }

  return partitions;
}
Run Code Online (Sandbox Code Playgroud)