Wyobraź sobie, że mam taką tablicę JS:
var a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
Chcę podzielić tę tablicę na N mniejszych tablic. Na przykład:
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]]
W przypadku Pythona mam to:
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
W przypadku JS najlepszym rozwiązaniem, jakie mogłem wymyślić, jest funkcja rekurencyjna, ale nie podoba mi się to, ponieważ jest skomplikowana i brzydka. Ta funkcja wewnętrzna zwraca tablicę taką jak ta [1, 2, 3, null, 4, 5, 6, null, 7, 8], a następnie muszę ponownie ją zapętlić i ręcznie podzielić. (Moją pierwszą próbą było zwrócenie tego: [1, 2, 3, [4, 5, 6, [7, 8, 9]]] i zdecydowałem się to zrobić z separatorem zerowym).
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));
}
Oto jsfiddle tego: http://jsfiddle.net/uduhH/
Jak byś to zrobił? Dzięki!
javascript
arrays
Tiago
źródło
źródło
split
funkcja nie jest daleko. Możesz usunąćnull
firmę, dodając dwa opakowania tablic:if (cols == 1) return [array]
ireturn [array.slice(0, size)].concat(split(array.slice(size), cols-1))
. Uważam, że ta rekurencyjna wersja jest znacznie bardziej czytelna niż większość odpowiedzi tutaj.Odpowiedzi:
Możesz sprawić, że plasterki będą „zbalansowane” (długości podtablic różnią się tak mniej, jak to możliwe) lub „równe” (wszystkie podtablice, ale ostatnia mają taką samą długość):
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>
źródło
var size = Math.ceil((len - i) / n--);
len - i
) przez liczbę pozostałych kawałków (n--
)Myślę, że ten sposób użycia splotu jest najczystszy:
splitToChunks(array, parts) { let result = []; for (let i = parts; i > 0; i--) { result.push(array.splice(0, Math.ceil(array.length / i))); } return result; }
Na przykład
parts = 3
weźmiesz 1/3, potem 1/2 pozostałej części, a następnie resztę tablicy.Math.ceil
zapewnia, że w przypadku nieparzystej liczby elementów trafią one do najwcześniejszych fragmentów.(Uwaga: to niszczy początkową tablicę).
źródło
function split(array, n) { let [...arr] = array; var res = []; while (arr.length) { res.push(arr.splice(0, n)); } return res; }
źródło
Właśnie wykonałem iteracyjną implementację algorytmu: http://jsfiddle.net/ht22q/ . To przechodzi twoje przypadki testowe.
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; }
źródło
Możesz zredukować to do matrycy. Poniższy przykład podzieli tablicę array (
arr
) na macierz tablic dwupozycyjnych. Jeśli chcesz inne rozmiary, po prostu zmień wartość 2 w drugiej linii:target.reduce((memo, value, index) => { if (index % 2 === 0 && index !== 0) memo.push([]) memo[memo.length - 1].push(value) return memo }, [[]])
Mam nadzieję, że to pomoże!
EDYCJA: Ponieważ niektórzy ludzie wciąż komentują, to nie odpowiada na pytanie, ponieważ ustalałem rozmiar każdego fragmentu zamiast liczby fragmentów, które chcę. Oto kod wyjaśniający, co próbuję wyjaśnić w sekcji komentarzy: Korzystanie z
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>') }
źródło
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; }, [[]]) }
Aktualizacja: 21.07.2020
Odpowiedź, której udzieliłem kilka lat temu, działa tylko wtedy, gdy
originalArray.length
<=numCols
. Możesz alternatywnie użyć czegoś podobnego do tej funkcji poniżej, ale stworzy to układ, który nie do końca pasuje do pytania (sortowanie poziome zamiast sortowania pionowego). AKA:[1,2,3,4]
->[[1,4],[2],[3]]
. Rozumiem, że to może nadal mieć wartość, więc zostawię to tutaj, ale polecam odpowiedź 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 }
Oryginalna odpowiedź z 2017 r .:
Stare pytanie, ale ponieważ vanillaJS nie jest wymaganiem, a tak wielu próbuje rozwiązać ten problem za pomocą lodash / chunk, i nie myląc się, co
_.chunk
tak naprawdę robi, oto zwięzłe + dokładne rozwiązanie wykorzystującelodash
:(W przeciwieństwie do zaakceptowanej odpowiedzi, gwarantuje to również n kolumn, nawet jeśli
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 }
for
Pętla na końcu jest co gwarantuje wymaganą liczbę „kolumny”.źródło
Podejście rekurencyjne, nie testowane.
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); } }
źródło
Inna rekurencyjna działa całkiem dobrze, jest mniej brzydka
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); }
źródło
function parseToPages(elements, pageSize = 8) { var result = []; while (elements.length) { result.push(elements.splice(0, pageSize)); } return result; }
źródło
Jeśli zdarzy się, że znasz wcześniej rozmiar kawałków, które chcesz, jest całkiem elegancki sposób na zrobienie tego w 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]));
Uważam, że ta notacja jest całkiem przydatna, na przykład do parsowania RGBA z pliku
Uint8ClampedArray
.źródło
groupsOfFour( [ 1 ] )
zwraca[ 1 , undefined, undefined, undefined ]
, a nie oczekiwane (i pożądane)[ [1] ]
.Prawdopodobnie czystsze podejście byłoby następujące (bez korzystania z żadnej innej biblioteki):
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);
Stworzyłem własną tablicę, która ma być podzielona. Możesz znaleźć kod tutaj
W bibliotece lodash mamy również metodę „chunk”, która jest bardzo przydatna. Mam nadzieję, że to pomoże
źródło
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; }
źródło
Ogólnie mówiąc, mutacja jest złą rzeczą ™.
To jest ładne, czyste i idempotentne.
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; }
źródło
Zrobiłem to w ten sposób, działa ...
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; }
źródło
sprawdź moją wersję tego podziału tablicy
// 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; };
źródło
jeśli wiesz, że chcesz ustawić child_arrays.length, to myślę, że to rozwiązanie jest najlepsze:
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
odpowiedź: [1, 2], [3, 4], [5, 6], [7, 8], [9, 10], [11]
źródło
Jeśli możesz używać
lodash
i chciałbyś mieć podejście do programowania funkcjonalnego, oto co wymyśliłem: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 }
źródło
wszystko powyżej może działać dobrze, ale co, jeśli masz
associative
tablicę z ciągami znaków jako kluczami?objectKeys = Object.keys; arraySplit(arr, n) { let counter = 0; for (const a of this.objectKeys(arr)) { this.arr[(counter%n)][a] = arr[a]; counter++; } }
źródło
Mam taki, który nie zmienia oryginalnej tablicy
function splitArray(array = [], nPieces = 1){ const splitArray = []; let atArrPos = 0; for(let i = 0; i < nPieces; i++){ const splitArrayLength = Math.ceil((array.length - atArrPos)/ (nPieces - i)); splitArray.push([]); splitArray[i] = array.slice(atArrPos, splitArrayLength + atArrPos); atArrPos += splitArrayLength; } return splitArray }
źródło
Możesz użyć prostej funkcji rekurencyjnej
const chunkify = (limit, completeArray, finalArray = [])=>{ if(!completeArray.length) return finalArray const a = completeArray.splice(0,limit); return chunkify(limit, completeArray, [...finalArray,a]) }
źródło
splitToChunks(arrayvar, parts) { let result = []; for (let i = parts; i > 0; i--) { result.push(arrayvar.splice(0, Math.ceil(arrayvar.length / i))); } return result; }
źródło
Po prostu użyj funkcji chunk lodash, aby podzielić tablicę na mniejsze tablice https://lodash.com/docs#chunk Nie musisz już majstrować przy pętlach!
źródło
Jeśli używasz lodash, możesz to dość łatwo osiągnąć, jak poniżej:
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]]
źródło
_.chunk
tworzy tablice N elementów, a nie N tablic. Twój przykład miałby wynik 6 tablic z 2 elementami w każdym z[[1, 2], [3, 4], [5, 6], [7, 8], [9, 10], [11]]