Разбить массив на куски
предположим, что у меня есть массив Javascript, который выглядит следующим образом:
["Element 1","Element 2","Element 3",...]; // with close to a hundred elements.
какой подход был бы уместен для разбиения (разделения) массива на множество меньших массивов с, скажем, 10 элементами не более?
30 ответов:
на массив.кусочек метод может извлечь срез из начала, середины или конца массива для любых целей, которые вам требуются, без изменения исходного массива.
var i,j,temparray,chunk = 10; for (i=0,j=array.length; i<j; i+=chunk) { temparray = array.slice(i,i+chunk); // do whatever }
изменено из ответа dbaseman:https://stackoverflow.com/a/10456344/711085
Object.defineProperty(Array.prototype, 'chunk_inefficient', { value: function(chunkSize) { var array=this; return [].concat.apply([], array.map(function(elem,i) { return i%chunkSize ? [] : [array.slice(i,i+chunkSize)]; }) ); } });Demo:
> [1,2,3,4,5,6,7].chunk_inefficient(3) [[1,2,3],[4,5,6],[7]]
незначительные дополнительное соглашение:
Я должен отметить, что это не элегантный (на мой взгляд) способ использования
Array.map. Он в основном делает следующее, Где ~ - это конкатенация:[[1,2,3]]~[]~[]~[] ~ [[4,5,6]]~[]~[]~[] ~ [[7]]он имеет то же асимптотическое время работы, что и метод ниже, но, возможно, хуже постоянный фактор из-за построения пустых списков. Можно было бы переписать это следующим образом (в основном так же, как метод Blazemonger, поэтому я изначально не представил этот ответ):
более эффективный метод:
Object.defineProperty(Array.prototype, 'chunk', { value: function(chunkSize) { var R = []; for (var i=0; i<this.length; i+=chunkSize) R.push(this.slice(i,i+chunkSize)); return R; } }); // refresh page if experimenting and you already defined Array.prototype.chunk
мой любимый способ в настоящее время является выше, или один из следующих:
Array.range = function(n) { // Array.range(5) --> [0,1,2,3,4] return Array.apply(null,Array(n)).map((x,i) => i) }; Object.defineProperty(Array.prototype, 'chunk', { value: function(n) { // ACTUAL CODE FOR CHUNKING ARRAY: return Array.range(Math.ceil(this.length/n)).map((x,i) => this.slice(i*n,i*n+n)); } });Demo:
> JSON.stringify( Array.range(10).chunk(3) ); [[1,2,3],[4,5,6],[7,8,9],[10]]или если вы не хотите, чтобы массив.функция диапазона, это на самом деле просто один лайнер (за исключением пух):
var ceil = Math.ceil; Object.defineProperty(Array.prototype, 'chunk', {value: function(n) { return Array(ceil(this.length/n)).fill().map((_,i) => this.slice(i*n,i*n+n)); }});или
Object.defineProperty(Array.prototype, 'chunk', {value: function(n) { return Array.from(Array(ceil(this.length/n)), (_,i)=>this.slice(i*n,i*n+n)); }});
старайтесь избегать ошибок с собственными прототипами, включая массив.прототип, если вы не знаете, кто будет потреблять ваш код (3-е лица, коллеги, себя позже и т. д.).
есть способы безопасного расширения прототипов (но не во всех браузерах) и есть способы безопасного использования объектов, созданных из расширенных прототипов, но лучше следовать эмпирическому правилу принцип наименьшего удивления и избегайте этих практик в целом.
Если у вас есть время, посмотрите выступление Эндрю Дюпона JSConf 2011, "все разрешено: расширение встроенных модулей", для хорошего обсуждения этой темы.
Но вернемся к вопросу, хотя приведенные выше решения будут работать, они слишком сложны и требуют ненужных вычислительных затрат. Вот мое решение:
function chunk (arr, len) { var chunks = [], i = 0, n = arr.length; while (i < n) { chunks.push(arr.slice(i, i += len)); } return chunks; } // Optionally, you can do the following to avoid cluttering the global namespace: Array.chunk = chunk;
вот версия ES6 с использованием reduce
perChunk = 2 // items per chunk inputArray = ['a','b','c','d','e'] inputArray.reduce((resultArray, item, index) => { const chunkIndex = Math.floor(index/perChunk) if(!resultArray[chunkIndex]) { resultArray[chunkIndex] = [] // start a new chunk } resultArray[chunkIndex].push(item) return resultArray }, []) // result: [['a','b'], ['c','d'], ['e']]и вы готовы к цепочке дальнейших преобразований карты/сокращения. Ваш входной массив остается нетронутым
Если вы предпочитаете более короткую, но менее читаемую версию, вы можете посыпать некоторые
concatв смеси для того же конечного результата:inputArray.reduce((all,one,i) => { const ch = Math.floor(i/perChunk); all[ch] = [].concat((all[ch]||[]),one); return all }, [])
Я бы предпочел использовать соединение способ:
var chunks = function(array, size) { var results = []; while (array.length) { results.push(array.splice(0, size)); } return results; };
старый вопрос: новый ответ! Я на самом деле работал с ответом на этот вопрос и имел друга улучшить его! Так вот оно:
Array.prototype.chunk = function ( n ) { if ( !this.length ) { return []; } return [ this.slice( 0, n ) ].concat( this.slice(n).chunk(n) ); }; [1,2,3,4,5,6,7,8,9,0].chunk(3); > [[1,2,3],[4,5,6],[7,8,9],[0]]
Я проверил различные ответы в jsperf.com. результат доступен там:http://jsperf.com/chunk-mtds
и самая быстрая функция (и это работает с IE8) - это:
function chunk(arr, chunkSize) { var R = []; for (var i=0,len=arr.length; i<len; i+=chunkSize) R.push(arr.slice(i,i+chunkSize)); return R; }
В настоящее время вы можете использовать функцию lodash ' chunk для разделения массива на меньшие массивыhttps://lodash.com/docs#chunk больше не нужно возиться с петлями!
ОДН-вкладыш в ECMA 6
const [list,chuckSize] = [[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15], 6] new Array(Math.ceil(list.length / chuckSize)).fill().map(_ => list.splice(0,chuckSize))
Хорошо, давайте начнем с довольно жесткой:
function chunk(arr, n) { return arr.slice(0,(arr.length+n-1)/n|0). map(function(c,i) { return arr.slice(n*i,n*i+n); }); }который используется следующим образом:
chunk([1,2,3,4,5,6,7], 2);после этого мы имеем эту плотную функцию редуктора:
function chunker(p, c, i) { (p[i/this|0] = p[i/this|0] || []).push(c); return p; }который используется следующим образом:
[1,2,3,4,5,6,7].reduce(chunker.bind(3),[]);так как котенок умирает, когда мы связываем
thisк числу, мы можем сделать ручное карринг как это вместо:// Fluent alternative API without prototype hacks. function chunker(n) { return function(p, c, i) { (p[i/n|0] = p[i/n|0] || []).push(c); return p; }; }который используется следующим образом:
[1,2,3,4,5,6,7].reduce(chunker(3),[]);тогда все еще довольно жесткая функция, которая делает все это в одном вперед:
function chunk(arr, n) { return arr.reduce(function(p, cur, i) { (p[i/n|0] = p[i/n|0] || []).push(cur); return p; },[]); } chunk([1,2,3,4,5,6,7], 3);
Я направлен на создание простого, не мутирует решение в чистом ЕС6. Особенности в JavaScript сделать это необходимо, чтобы заполнить пустой массив перед сопоставлением :-(
function chunk(a, l) { return new Array(Math.ceil(a.length / l)).fill(0) .map((_, n) => a.slice(n*l, n*l + l)); }эта версия с рекурсией кажется проще и убедительнее:
function chunk(a, l) { if (a.length == 0) return []; else return [a.slice(0, l)].concat(chunk(a.slice(l), l)); }смехотворно слабые функции массива ES6 делает для хороших головоломок: -)
если вы используете версию EcmaScript >= 5.1, вы можете реализовать функциональную версию
chunk()используя массив.уменьшить() который имеет O (N) сложность:function chunk(chunkSize, array) { return array.reduce(function(previous, current) { var chunk; if (previous.length === 0 || previous[previous.length -1].length === chunkSize) { chunk = []; // 1 previous.push(chunk); // 2 } else { chunk = previous[previous.length -1]; // 3 } chunk.push(current); // 4 return previous; // 5 }, []); // 6 } console.log(chunk(2, ['a', 'b', 'c', 'd', 'e'])); // prints [ [ 'a', 'b' ], [ 'c', 'd' ], [ 'e' ] ]объяснение каждого
// nbrвыше:
- создайте новый фрагмент, если Предыдущее значение, т. е. ранее возвращенный массив фрагментов, пуст или если последний предыдущий фрагмент имеет
chunkSizeпредметы- Добавить новый кусок в массив существующих куски
- в противном случае текущий фрагмент является последним фрагментом в массиве фрагментов
- добавить текущее значение в блок
- возвращает измененный массив фрагментов
- инициализируйте сокращение, передав пустой массив
карринг на основе
chunkSize:var chunk3 = function(array) { return chunk(3, array); }; console.log(chunk3(['a', 'b', 'c', 'd', 'e'])); // prints [ [ 'a', 'b', 'c' ], [ 'd', 'e' ] ]
вы можете добавить глобальные
in coffeescript: b = (a.splice(0, len) while a.length) demo a = [1, 2, 3, 4, 5, 6, 7] b = (a.splice(0, 2) while a.length) [ [ 1, 2 ], [ 3, 4 ], [ 5, 6 ], [ 7 ] ]
было много ответов, но это то, что я использую:
const chunk = (arr, size) => arr .reduce((acc, _, i) => (i % size) ? acc : [...acc, arr.slice(i, i + size)] , []) // USAGE const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] chunk(numbers, 3) // [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]]во-первых, проверьте остаток при делении индекса на размер куска.
Если есть остаток, то просто верните массив аккумулятора.
Если остатка нет, то индекс делится на размер куска, поэтому возьмите срез из исходного массива (начиная с текущего индекса) и добавьте его в массив накопителя.
Итак, возвращенный массив аккумулятора для каждая итерация сокращения выглядит примерно так:
// 0: [[1, 2, 3, 4]] // 1: [[1, 2, 3, 4]] // 2: [[1, 2, 3, 4]] // 3: [[1, 2, 3, 4]] // 4: [[1, 2, 3, 4], [5, 6, 7, 8]] // 5: [[1, 2, 3, 4], [5, 6, 7, 8]] // 6: [[1, 2, 3, 4], [5, 6, 7, 8]] // 7: [[1, 2, 3, 4], [5, 6, 7, 8]] // 8: [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10]] // 9: [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10]]
создал пакет npm для этого https://www.npmjs.com/package/array.chunk
var result = []; for (var i = 0; i < arr.length; i += size) { result.push(arr.slice(i, size + i)); } return result;
ES6 однострочный подход на основе
Array.prototypereduceиpushметоды:const doChunk = (list, size) => list.reduce((r, v) => (!r.length || r[r.length - 1].length === size ? r.push([v]) : r[r.length - 1].push(v)) && r , []); console.log(doChunk([0,1,2,3,4,5,6,7,8,9,10,11,12], 5)); // [[0, 1, 2, 3, 4], [5, 6, 7, 8, 9], [10, 11, 12]]
и это было бы моим вкладом в эту тему. Я думаю
.reduce()- это лучший способ.var segment = (arr, n) => arr.reduce((r,e,i) => i%n ? (r[r.length-1].push(e), r) : (r.push([e]), r), []), arr = Array.from({length: 31}).map((_,i) => i+1); res = segment(arr,7); console.log(JSON.stringify(res));но вышеприведенная реализация не очень эффективна, так как
.reduce()проходит через все
вот не мутирующее решение, использующее только рекурсию и slice().
const splitToChunks = (arr, chunkSize, acc = []) => ( arr.length > chunkSize ? splitToChunks( arr.slice(chunkSize), chunkSize, [...acc, arr.slice(0, chunkSize)] ) : [...acc, arr] );тогда просто используйте его как
splitToChunks([1, 2, 3, 4, 5], 3)и[[1, 2, 3], [4, 5]].вот скрипка для вас, чтобы попробовать: https://jsfiddle.net/6wtrbx6k/2/
ES6 генератор версия
function* chunkArray(array,size=1){ var clone = array.slice(0); while (clone.length>0) yield clone.splice(0,size); }; var a = new Array(100).fill().map((x,index)=>index); for(const c of chunkArray(a,10)) console.log(c);
Это самое эффективное и прямое решение, которое я мог бы придумать:
function chunk(array, chunkSize) { let chunkCount = Math.ceil(array.length / chunkSize); let chunks = new Array(chunkCount); for(let i = 0, j = 0, k = chunkSize; i < chunkCount; ++i) { chunks[i] = array.slice(j, k); j = k; k += chunkSize; } return chunks; }
EDIT: @mblase75 добавил более краткий код к более раннему ответу, пока я писал свой, поэтому я рекомендую пойти с его решением.
Вы можете использовать такой код:
var longArray = ["Element 1","Element 2","Element 3", /*...*/]; var smallerArrays = []; // will contain the sub-arrays of 10 elements each var arraySize = 10; for (var i=0;i<Math.ceil(longArray.length/arraySize);i++) { smallerArrays.push(longArray.slice(i*arraySize,i*arraySize+arraySize)); }изменить значение
arraySizeчтобы изменить максимальную длину меньших массивов.
Я немного изменил BlazeMonger, чтобы использовать для объекта jQuery..
var $list = $('li'), $listRows = []; for (var i = 0, len = $list.length, chunk = 4, n = 0; i < len; i += chunk, n++) { $listRows[n] = $list.slice(i, i + chunk); }
Я создал следующее JSFiddle продемонстрировать мой подход к вашему вопросу.
(function() { // Sample arrays var //elements = ["0", "1", "2", "3", "4", "5", "6", "7"], elements = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "40", "41", "42", "43"]; var splitElements = [], delimiter = 10; // Change this value as needed // parameters: array, number of elements to split the array by if(elements.length > delimiter){ splitElements = splitArray(elements, delimiter); } else { // No need to do anything if the array's length is less than the delimiter splitElements = elements; } //Displaying result in console for(element in splitElements){ if(splitElements.hasOwnProperty(element)){ console.log(element + " | " + splitElements[element]); } } })(); function splitArray(elements, delimiter) { var elements_length = elements.length; if (elements_length > delimiter) { var myArrays = [], // parent array, used to store each sub array first = 0, // used to capture the first element in each sub array index = 0; // used to set the index of each sub array for (var i = 0; i < elements_length; ++i) { if (i % delimiter === 0) { // Capture the first element of each sub array from the original array, when i is a modulus factor of the delimiter. first = i; } else if (delimiter - (i % delimiter) === 1) { // Build each sub array, from the original array, sliced every time the i one minus the modulus factor of the delimiter. index = (i + 1) / delimiter - 1; myArrays[index] = elements.slice(first, i + 1); } else if(i + 1 === elements_length){ // Build the last sub array which contain delimiter number or less elements myArrays[index + 1] = elements.slice(first, i + 1); } } // Returned is an array of arrays return myArrays; } }прежде всего, у меня есть два примера: массив с менее чем восемью элементами, другой с массивом с более чем восемью элементами (прокомментируйте тот, который вы не хотите использовать).
затем я проверяю размер массива, простой, но необходимый, чтобы избежать дополнительных вычислений. Отсюда, если массив соответствует критерии (размер массива >
delimiter) переходим в
Я просто написал это с помощью функции groupBy.
// utils const group = (source) => ({ by: (grouping) => { const groups = source.reduce((accumulator, item) => { const name = JSON.stringify(grouping(item)); accumulator[name] = accumulator[name] || []; accumulator[name].push(item); return accumulator; }, {}); return Object.keys(groups).map(key => groups[key]); } }); const chunk = (source, size) => group(source.map((item, index) => ({ item, index }))) .by(x => Math.floor(x.index / size)) .map(x => x.map(v => v.item)); // 103 items const arr = [6,2,6,6,0,7,4,9,3,1,9,6,1,2,7,8,3,3,4,6,8,7,6,9,3,6,3,5,0,9,3,7,0,4,1,9,7,5,7,4,3,4,8,9,0,5,1,0,0,8,0,5,8,3,2,5,6,9,0,0,1,5,1,7,0,6,1,6,8,4,9,8,9,1,6,5,4,9,1,6,6,1,8,3,5,5,7,0,8,3,1,7,1,1,7,6,4,9,7,0,5,1,0]; const chunks = chunk(arr, 10); console.log(JSON.stringify(chunks));
вот мой подход, используя понимание списка Coffeescript. Отличная статья с подробным пониманием в Coffeescript может быть найти здесь.
chunk: (arr, size) -> chunks = (arr.slice(index, index+size) for item, index in arr by size) return chunks
Это должен быть простой ответ без многих математических осложнений.
function chunkArray(array, sizeOfTheChunkedArray) { const chunked = []; for (let element of array) { const last = chunked[chunked.length - 1]; if(!last || last.length === sizeOfTheChunkedArray) { chunked.push([element]) } else { last.push(element); } } return chunked; }
Привет попробуйте это -
function split(arr, howMany) { var newArr = []; start = 0; end = howMany; for(var i=1; i<= Math.ceil(arr.length / howMany); i++) { newArr.push(arr.slice(start, end)); start = start + howMany; end = end + howMany } console.log(newArr) } split([1,2,3,4,55,6,7,8,8,9],3)
# in coffeescript # assume "ar" is the original array # newAr is the new array of arrays newAr = [] chunk = 10 for i in [0... ar.length] by chunk newAr.push ar[i... i+chunk] # or, print out the elements one line per chunk for i in [0... ar.length] by chunk console.log ar[i... i+chunk].join ' '
Comments