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

IT技术 javascript arrays
2021-02-03 05:27:04

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

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

我想要的是将该数组拆分为 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]]

对于 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

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

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

你会怎么做?谢谢!

6个回答

您可以使切片“平衡”(子数组的长度差异尽可能小)或“偶数”(所有子数组但最后一个具有相同的长度):

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>

@cbdeveloper:给你 function chunkify<T>(a: T[], n: number, balanced: boolean): T[][]
2021-03-24 05:27:04
嗨@georg 谢谢。我将如何修改此代码以确保除最后一个子数组之外的所有子数组长度相等(当然,除数导致没有余数,因此所有子数组都相等)。我很感激任何帮助。
2021-04-04 05:27:04
你的解决方案很整洁,它和我的递归解决方案做同样的事情,但没有那么混乱。谢谢!
2021-04-06 05:27:04
像魅力一样工作..很好的解决方案
2021-04-06 05:27:04
嗨@georg,你能解释一下这一行吗: var size = Math.ceil((len - i) / n--);
2021-04-10 05:27:04

我认为这种使用 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;
}

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

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

这个解决方案对我有用。只有一个建议。为了不破坏初始数组,添加这一行const copyArray = array.map(v => v);来创建数组的浅拷贝。然后在函数的其余部分操作复制的数组。
2021-04-02 05:27:04
稍加修改。它也会创建一个副本。const copyArray = [...array]
2021-04-09 05:27:04

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

请添加一些解释为什么此代码有助于 OP。这将有助于为未来的观众提供一个可以学习的答案。有关详细信息,请参阅如何回答
2021-03-14 05:27:04
这不会拆分为 n 个子数组,仅拆分为 n 个长度的子数组。
2021-03-22 05:27:04
对于 n = 5 和 arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],这不会按预期工作。
2021-04-03 05:27:04
要解决 OPs 问题,您将它与 split(arr, Math.ceil(arr.length/chunkCount)) 一起使用,我猜...但我来这里是为了寻找拆分为 N 大小的块,因此非常适合:)
2021-04-08 05:27:04

我刚刚对算法进行了迭代实现: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;
}
(这按预期工作,但我只能选择一个正确的答案)嗨!谢谢你的帮助。很好地思考如何使用其余的。
2021-04-06 05:27:04

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

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

希望能帮助到你!

编辑:因为有些人仍在评论这并没有回答问题,因为我正在修复每个块大小而不是我想要的块数这是解释我在评论部分试图解释的内容的代码:使用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>') }

喜欢这个 !!!我已经重构以返回均匀大小的块 function splitArr(arr, n) { return arr.reduce(function (a, i) { if (a[a.length - 1].length >= arr.length / n) { a.push([]) } a[a.length - 1].push(i) return a; }, [[]]) }
2021-03-17 05:27:04
我喜欢这种技术,但它不能回答问题。它返回任意数量的 x 大小的块,而问题是要求 x 数量的均匀大小的块。
2021-03-22 05:27:04
简洁且非常聪明,这是我用这个简单的模式解决这个问题和其他案例的首选方法,谢谢!
2021-04-02 05:27:04
哇,好简洁的方法来做到这一点!爱它!很好!:-)
2021-04-08 05:27:04
肯定没有回答这个问题。
2021-04-10 05:27:04