Разбить массив на куски

516

Допустим, у меня есть массив Javascript, который выглядит следующим образом:

["Element 1","Element 2","Element 3",...]; // with close to a hundred elements.

Какой подход был бы уместен для разбиения массива на множество меньших массивов, скажем, с 10 элементами максимум?

промышленные
источник
Возможный дубликат разбиения массива JS на N массивов
TJ
24
Для пользователей lodash вы ищете _.chunk .
Ulysse BN
2
Я только что попробовал, const chunk = (arr, n) => arr.length ? [arr.slice(0, n), ...chunk(arr.slice(n), n)] : []что красиво и коротко, но, кажется, занимает примерно 256 × столько же, сколько ответ @ AymKdn для 1000 элементов, и 1058 × столько же для 10 000 элементов!
Toph
если вам нужен минимальный размер последнего фрагмента, вот варианты: stackoverflow.com/questions/57908133/…
Ахмет

Ответы:

643

Метод array.slice может извлечь фрагмент из начала, середины или конца массива для любых целей, без каких-либо изменений исходного массива.

var i,j,temparray,chunk = 10;
for (i=0,j=array.length; i<j; i+=chunk) {
    temparray = array.slice(i,i+chunk);
    // do whatever
}
Blazemonger
источник
19
Помните, если это - функция утилит, чтобы утверждать против chunkбытия 0. (бесконечный цикл)
Стивен Лу
19
Нет, последний кусок должен быть меньше остальных.
Blazemonger
7
@ Blazemonger, действительно! В следующий раз я сам попробую, прежде чем делать выводы. Я предположил (неправильно), что передача входных данных в array.slice, превышающая границы массива, будет проблемой, но она работает просто отлично!
rysqui
55
Для однострочных (цепные любовники) : const array_chunks = (array, chunk_size) => Array(Math.ceil(array.length / chunk_size)).fill().map((_, index) => index * chunk_size).map(begin => array.slice(begin, begin + chunk_size));.
Константин Ван
8
Зачем тебе j? Сначала я подумал, что это оптимизация, но на самом деле она медленнее, чем для (i = 0; i <array.length; i ++) {}
Alex
149

Изменено в ответе 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)];
      })
    );
  }
});

console.log(
  [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, поэтому я изначально не представил этот ответ):

Более эффективный метод:

// refresh page if experimenting and you already defined Array.prototype.chunk

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

console.log(
  [1, 2, 3, 4, 5, 6, 7].chunk(3)
)


Мой предпочтительный способ в настоящее время - выше или один из следующих:

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));

  }
});

Демо-версия:

> JSON.stringify( Array.range(10).chunk(3) );
[[1,2,3],[4,5,6],[7,8,9],[10]]

Или, если вам не нужна функция Array.range, это просто однострочная (исключая пух):

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));
}});
ninjagecko
источник
41
Эх, я бы не стал возиться с прототипом, поскольку ощущение прохлады, которое вы получаете от вызова chunkфункции в массиве, на самом деле не перевешивает дополнительную сложность, которую вы добавляете, и незаметные ошибки, которые могут возникнуть при работе со встроенными прототипами.
Гордон Густафсон
12
Он не балуется с ними, он продлевает их для Массивов. Я понимаю, никогда не трогая Object.prototype, потому что это будет пузыриться для всех объектов (все), но для этой специфической функции массива я не вижу никаких проблем.
rlemon
Уверен , что должно быть array.map(function(i)не array.map(function(elem,i)хотя
Nigel Angel
3
На основе таблицы совместимости на сайте разработчика Mozilla, Array.map для IE9 +. Быть осторожен.
Майкель D
Будьте осторожны, чтобы пропустить плавающие числа типа 7,5 - привести к непредсказуемым кускам
Гал Браха
103

Вот версия ES6, использующая уменьшение

var perChunk = 2 // items per chunk    

var inputArray = ['a','b','c','d','e']

var result = 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
}, [])

console.log(result); // 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
}, [])
Андрей Р
источник
Это кажется наиболее сжатым решением. Что получает chunkIndex = Math.floor (index / perChunk)? Это среднее?
me-me
5/2 = 2.5и Math.floor(2.5) = 2поэтому элемент с индексом 5 будет помещен в корзину 2
Андрей R
Спасибо Andrei R. Ах, так что он проходит через 0 - 2. Так как это называется с точки зрения математики? Я предполагаю, что моя точка зрения заключается в том, что я никогда бы не подумал разделить index / 2 на каждый индекс, чтобы получить индекс для каждого среза. Поэтому я пытаюсь обернуть это вокруг головы, потому что мне это действительно нравится, но я не до конца понимаю это с точки зрения математики. Я обычно делаю это, чтобы получить средние значения от общего числа, но это выглядит иначе.
я-я
Это решение неэффективно по сравнению с другими решениями, потому что вам нужно перебирать каждый элемент.
JP de la Torre
вы правы @JPdelaTorre, вероятно, он не так эффективен, как решения, которые нарезают массивы, но вы тут раскалываетесь. большинство перечисленных ответов будет неэффективным по этому определению.
Андрей Р
102

Старайтесь избегать хакерства с нативными прототипами, включая Array.prototype, если вы не знаете, кто будет потреблять ваш код (третьи лица, коллеги, вы позже и т. Д.).

Существуют способы безопасного расширения прототипов (но не во всех браузерах), и есть способы безопасного использования объектов, созданных из расширенных прототипов, но лучшим практическим правилом является следовать принципу наименьшего сюрприза и вообще избегать этих практик.

Если у вас есть время, посмотрите презентацию Andon Dupont 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;
furf
источник
25
«Избегайте гадости с нативными прототипами». Новые разработчики JS должны получить временную, если не постоянную, татуировку этой фразы.
Джейкоб Далтон
2
Я использую javascript в течение многих лет и почти не трачу время на работу с прототипом, на самые вызывающие функции, никогда не изменяя, как видят некоторые люди.
1984
2
лучшее предложение в моих глазах, самое простое для понимания и реализации, большое спасибо!
Ольга Фарбер
1
@JacobDalton Я думаю, что это вина всех университетов. Люди думают, что ООП должен использоваться везде. Поэтому они боятся «просто создать функцию». Они хотят быть уверены, что поместили это внутрь чего-то. Даже если это совсем не подходит. Если нет точечной нотации, нет «архитектуры».
Герман
51

Я проверил разные ответы на jsperf.com. Результат доступен там: https://web.archive.org/web/20150909134228/https://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;
}
AymKdn
источник
1
Спасибо @AymKdn за этот тест: это было так полезно! Я использовал сплайс-подход, и он сломал мой браузер Chrome v70 с размером фрагмента 884432. С вашим предложенным подходом «среза» мой код больше не прерывает процесс «рендеринга» браузера. :)
Бенни Нойгебауэр,
Вот машинописная версия этого: function chunk<T>(array: T[], chunkSize: number): T[][] { const R = []; for (let i = 0, len = array.length; i < len; i += chunkSize) R.push(array.slice(i, i + chunkSize)); return R; }
MacKinley Smith
34

Я бы предпочел использовать метод сплайсинга :

var chunks = function(array, size) {
  var results = [];
  while (array.length) {
    results.push(array.splice(0, size));
  }
  return results;
};
Арек Флиник
источник
16
Нужно быть осторожным с этим соединением, поскольку оно изменяет исходный массив, поэтому обязательно четко документируйте побочные эффекты.
bdrx
3
Тогда используйте вместо этого ломтик
mplungjan
@mplungjan Результатом будет один и тот же массив снова и снова при использовании слайса. Так что это не просто замена без дополнительных модификаций.
nietonfir
Единственное, что я бы добавил к этому ответу, - это клон исходного массива. Я бы сделал это с помощью оператора распространения ES6. var clone = [...array]затем выполните проверку длины и сращивание этого клонированного массива.
MauricioLeal
1
Или, если вы не можете использовать функции ES6, вы можете просто array = array.slice()создать мелкую копию.
3limin4t0r
34

Однострочник в 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))
Шайрон Толедо
источник
9
он изменяет исходный listмассив
Jacka
6
Легко исправить, используя .slice () .. .map((_,i) => list.slice(i*chuckSize,i*chuckSize+chuckSize))
Джеймс Роби
2
В JSPerf это значительно более эффективно, чем во многих других ответах.
Мика Хеннинг
30

Старый вопрос: новый ответ! Я на самом деле работал с ответом на этот вопрос, и мой друг улучшил его! Итак, вот оно:

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]]
rlemon
источник
14
Кстати, производительность этого метода равна O (N ^ 2), поэтому его не следует использовать в критичных для производительности разделах кода или с длинными массивами (в частности, когда .lengthразмер массива намного превышает размер фрагмента n). Если бы это был ленивый язык (в отличие от javascript), этот алгоритм не страдал бы за O (N ^ 2) времени. Тем не менее, рекурсивная реализация элегантна. Вероятно, вы можете изменить его, чтобы повысить производительность, сначала определив вспомогательную функцию, которая повторяется array,position, а затем отправив: Array.prototype.chunkвозвращает [вашу вспомогательную функцию] (...)
ninjagecko
спасибо, сэнсэй, за то, что благословил меня своим духом, что я, должно быть, сегодня вечером перезвонил
укусил
1
или ...var chunk = (arr, n) => { if ( !arr.length ) return []; return [ arr.slice( 0, n ) ].concat( chunk(arr.slice(n), n) ) }
Эд Уильямс
28

В настоящее время вы можете использовать функцию lodash 'chunk, чтобы разделить массив на меньшие массивы. Https://lodash.com/docs#chunk Больше не нужно возиться с циклами!

Георгий Геролянц
источник
3
Я чувствую, что на вопросы javascript должен быть отказ от ответственности: вы пробовали lodash? Практически первое, что я включаю в узел или браузер.
Джейкоб Далтон
20

Было много ответов, но это то, что я использую:

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]]

Сначала проверьте остаток при делении индекса на размер куска.

Если есть остаток, просто верните массив аккумуляторов.

Если остатка нет, то индекс делится на размер фрагмента, поэтому возьмите фрагмент из исходного массива (начиная с текущего индекса) и добавьте его в массив аккумулятора.

Итак, возвращаемый массив аккумуляторов для каждой итерации Reduce выглядит примерно так:

// 0: [[1, 2, 3]]
// 1: [[1, 2, 3]]
// 2: [[1, 2, 3]]
// 3: [[1, 2, 3], [4, 5, 6]]
// 4: [[1, 2, 3], [4, 5, 6]]
// 5: [[1, 2, 3], [4, 5, 6]]
// 6: [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
// 7: [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
// 8: [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
// 9: [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]]
Стив Холгадо
источник
Хорошее решение и хорошее визуальное представление итераций. Я получил очень похожее решение, которое отправил в ответ: stackoverflow.com/a/60779547
mts knn
15

Использование генераторов

function* chunks(arr, n) {
 for(let i = 0; i < arr.length; i += n) {
     yield(arr.slice(i, i+n));
     }
}
let someArray = [0,1,2,3,4,5,6,7,8,9]
console.log([...chunks(someArray, 2)]) // [[0,1],[2,3],[4,5],[6,7],[8,9]]

Икечукву Эз
источник
3
Я был удивлен всеми ответами на этот вопрос, которые почти использовали генераторы или использовали их более сложными способами. Хорошая краткость и производительность с этим решением.
Кит
14

Хорошо, давайте начнем с довольно узкого:

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);
user239558
источник
Не работает в iE8.
Nadeemmnn Mohd
4
ХА! Я люблю комментарий котенка. извините за дополнительный конструктивный вклад :)
29
Я бы сделал (p[i/n|0] || (p[i/n|0] = [])), чтобы вы не присваивали значение, если не нужно ...
yckart
12

Я стремился создать простое решение без мутаций в чистом ES6. Особенности в 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 делают для хороших загадок :-)

thoredge
источник
1
Я также написал мой так же, как это. Это все еще работает, если вы удалите 0из fill, что делает fillвзгляд немного более осмысленным, imho.
Коерт Гроббелаар,
12

Я думаю, что это хорошее рекурсивное решение с синтаксисом ES6:

const chunk = function(array, size) {
  if (!array.length) {
    return [];
  }
  const head = array.slice(0, size);
  const tail = array.slice(size);

  return [head, ...chunk(tail, size)];
};

console.log(chunk([1,2,3], 2));

Гергели Фехервари
источник
9

Создан пакет 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;

При использовании TypedArray

var result = [];

for (var i = 0; i < arr.length; i += size) {
  result.push(arr.subarray(i, size + i));
}
return result;
zhiyelee
источник
@ A1rPun Мой плохой, я не добавил туда комментарий. Да, нет sliceметода для TypedArray, мы можем использовать subarrayвместо developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
zhiyelee
8

Если вы используете версию EcmaScript> = 5.1, вы можете реализовать функциональную версию с chunk()использованием array.reduce ( ) со сложностью 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выше:

  1. Создайте новый чанк, если предыдущее значение, т. Е. Ранее возвращенный массив чанков, пусто или если последний предыдущий чанк имеет chunkSizeэлементы
  2. Добавить новый чанк в массив существующих чанков
  3. В противном случае текущий чанк является последним чанком в массиве чанков.
  4. Добавить текущее значение к чанку
  5. Вернуть измененный массив чанков
  6. Инициализируйте сокращение, передав пустой массив

Карри на основе chunkSize:

var chunk3 = function(array) {
    return chunk(3, array);
};

console.log(chunk3(['a', 'b', 'c', 'd', 'e']));
// prints [ [ 'a', 'b', 'c' ], [ 'd', 'e' ] ]

Вы можете добавить chunk()функцию к глобальному Arrayобъекту:

Object.defineProperty(Array.prototype, 'chunk', {
    value: function(chunkSize) {
        return this.reduce(function(previous, current) {
            var chunk;
            if (previous.length === 0 || 
                    previous[previous.length -1].length === chunkSize) {
                chunk = [];
                previous.push(chunk);
            }
            else {
                chunk = previous[previous.length -1];
            }
            chunk.push(current);
            return previous;
        }, []);
    }
});

console.log(['a', 'b', 'c', 'd', 'e'].chunk(4));
// prints [ [ 'a', 'b', 'c' 'd' ], [ 'e' ] ]

matsev
источник
7
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 ] ]
Арпит Джайн
источник
a.splice (0, 2) удаляет вложенный массив a [0..1] из a и возвращает вложенный массив a [0..1]. Я делаю массив из всех этих массивов
Arpit Jain
2
Я рекомендую использовать неразрушающий метод slice () вместо splice ()
Ash Blue
7
results = []
chunk_size = 10
while(array.length > 0){
   results.push(array.splice(0, chunk_size))
}
Джон
источник
1
Не уверен, почему за это проголосовали, но код может использовать некоторые объяснения.
jpaugh
2
Потому что сплайс разрушает исходный массив.
Металим
7

Следующий подход ES2015 работает без определения функции и непосредственно для анонимных массивов (пример с размером чанка 2):

[11,22,33,44,55].map((_, i, all) => all.slice(2*i, 2*i+2)).filter(x=>x.length)

Если вы хотите определить функцию для этого, вы можете сделать это следующим образом (улучшив комментарий K ._ к ответу Blazemonger ):

const array_chunks = (array, chunk_size) => array
    .map((_, i, all) => all.slice(i*chunk_size, (i+1)*chunk_size))
    .filter(x => x.length)
user1460043
источник
6

И это был бы мой вклад в эту тему. Я думаю, .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()проходит через все arrфункции. Более эффективный подход (очень близкий к самому быстрому императивному решению) заключался бы в итерации по уменьшенному (подлежащему фрагментации) массиву, поскольку мы можем заранее рассчитать его размер Math.ceil(arr/n);. Как только мы получим пустой массив результатов, как Array(Math.ceil(arr.length/n)).fill();остальное, нужно отобразить в него фрагменты arrмассива.

function chunk(arr,n){
  var r = Array(Math.ceil(arr.length/n)).fill();
  return r.map((e,i) => arr.slice(i*n, i*n+n));
}

arr = Array.from({length: 31},(_,i) => i+1);
res = chunk(arr,7);
console.log(JSON.stringify(res));

Redu
источник
5

Используйте кусок от lodash

lodash.chunk(arr,<size>).forEach(chunk=>{
  console.log(chunk);
})
Милинд Чаудхари
источник
5

Еще одно решение с использованием arr.reduce():

const chunk = (arr, size) => (
  arr.reduce((acc, _, i) => {
    if (i % size === 0) acc.push(arr.slice(i, i + size))
    return acc
  }, [])
)

// Usage:
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
const chunked = chunk(numbers, 3)
console.log(chunked)

Это решение очень похоже на решение Стива Холгадо . Однако, поскольку это решение не использует расширение массива и не создает новые массивы в функции редуктора, оно быстрее (см. Тест jsPerf ) и субъективно более читабельно (более простой синтаксис), чем другое решение.

На каждой n-й итерации (где n = size; начиная с первой итерации) к массиву-аккумулятору ( acc) добавляется фрагмент массива (arr.slice(i, i + size) ), а затем возвращается. На других итерациях массив аккумуляторов возвращается как есть.

Если sizeноль, метод возвращает пустой массив. Если значение sizeотрицательное, метод возвращает неверные результаты. Поэтому, если это необходимо в вашем случае, вы можете сделать что-то с отрицательными или неположительными sizeзначениями.


Если в вашем случае важна скорость, простой forцикл будет быстрее, чем при использовании arr.reduce()(см. Тест jsPerf ), и некоторые могут найти этот стиль также более читабельным:

function chunk(arr, size) {
  // This prevents infinite loops
  if (size < 1) throw new Error('Size must be positive')

  const result = []
  for (let i = 0; i < arr.length; i += size) {
    result.push(arr.slice(i, i + size))
  }
  return result
}
MTS KNN
источник
4

Для функционального решения, используя Ramda :

Где popularProductsваш входной массив, 5это размер чанка

import splitEvery from 'ramda/src/splitEvery'

splitEvery(5, popularProducts).map((chunk, i) => {
// do something with chunk

})

Дамиан Грин
источник
4

ES6 однострочный подход на основе Array.prototype reduceи 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]]
dhilt
источник
4

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);
Rm558
источник
Используйте constвместо этого.
Константин Ван
4

Это самое эффективное и простое решение, которое я мог придумать:

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;
}
mpen
источник
4

ES6 распространяет функционал #ohmy #ftw

const chunk =
  (size, xs) => 
    xs.reduce(
      (segments, _, index) =>
        index % size === 0 
          ? [...segments, xs.slice(index, index + size)] 
          : segments, 
      []
    );

console.log( chunk(3, [1, 2, 3, 4, 5, 6, 7, 8]) );

goofballLogic
источник
4

Вот рекурсивное решение, которое является хвостовым вызовом оптимизации.

const splitEvery = (n, xs, y=[]) =>
  xs.length===0 ? y : splitEvery(n, xs.slice(n), y.concat([xs.slice(0, n)])) 

console.log(splitEvery(2, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]))

Крис Вуга
источник
2

РЕДАКТИРОВАТЬ: @ 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чтобы изменить максимальную длину меньших массивов.

dentaku
источник
2

Вот немутантное решение, использующее только рекурсию и 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/

Сухайр Заин
источник