Разбиение массива JS на N массивов
представьте, что у меня есть массив 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]]], и я решил сделать это с помощью разделителя 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));
}
вот jsfiddle этого:http://jsfiddle.net/uduhH/
как бы вы это сделали? Спасибо!
17 ответов:
вы можете сделать срезы "сбалансированными" (длины подрешеток отличаются как можно меньше) или "четными" (все подрешетки, но последние имеют одинаковую длину):
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>
Я только что сделал итерационную реализацию алгоритма: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; }
function split(arr, n) { var res = []; while (arr.length) { res.push(arr.splice(0, n)); } return res; }
вы можете уменьшить его в матрицу. Приведенный ниже пример разбивает массив (
arr
) в матрицу двухпозиционных массивов. Если вы хотите другие размеры, просто измените значение 2 на второй строке:target.reduce((memo, value, index) => { if (index % 2 == 0 && index !== 0) memo.push([]) memo[memo.length - 1].push(value) return memo }, [[]])
надеюсь, что это помогает!
EDIT: потому что некоторые люди все еще комментируют это не отвечает на вопрос, так как я исправлял размер каждого куска вместо количества кусков Я хочу. Вот он код, объясняющий то, что я пытаюсь объяснить в раздел комментариев: использование
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>') }
старый вопрос, но поскольку vanillaJS не является требованием, и поэтому многие пытаются решить это с помощью lodash / chunk, и не ошибаясь, что
_.chunk
на самом деле делает, вот краткое + точное решение с помощьюlodash
:(в отличие от принятого ответа, это также гарантирует n столбцов, даже если
originalArray.length
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
петля в конце-это то, что гарантирует желаемое количество "столбцов".
Я думаю, что этот путь с помощью соединения является самым чистым:
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
гарантирует, что в случае неравномерного количества элементов они перейдут к самым ранним кускам.(Примечание: это разрушает исходный массив.)
рекурсивный подход, не проверял.
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); } }
другой рекурсивный работает довольно хорошо, он менее уродлив
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); }
вероятно, более чистый подход будет следующим (без использования какой-либо другой библиотеки) :
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);
Я создал свой собственный массив, который должен быть поблочное. Вы можете найти код здесь
также у нас есть метод "chunk" в библиотеке lodash, который очень полезен. Надеюсь, что это поможет
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; }
Я сделал это таким образом, это работает...
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; }
Проверьте мою версию этого массива 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; };
Если вы знаете, хотите установить child_arrays.тогда я думаю, что это решение лучшим:
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; }
вызов fn: СП(2, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]) //2 - child_arrat.длина
ответ: [1, 2], [3, 4], [5, 6], [7, 8], [9, 10], [11]
просто используйте функцию lodash ' chunk, чтобы разделить массив на меньшие массивыhttps://lodash.com/docs#chunk больше не нужно возиться с петлями!
Если вы можете использовать
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 }