Розділити масив на шматки


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 елементів, і 1,058 × до 10 000 елементів!
Тоф

якщо вам також потрібен мінімальний розмір останньої частини, ось такі варіанти: stackoverflow.com/questions/57908133/…
Ahmet Cetin

Відповіді:


640

Метод 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
}

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

Він має такий самий асимптотичний час роботи, що і метод нижче, але, можливо, гірший постійний коефіцієнт через побудову порожніх списків. Можна було б переписати це так (здебільшого так само, як метод Блаземонгера, саме тому я спочатку не подав цю відповідь):

Більш ефективний метод:

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

41
Так, я б уникав возитися з прототипом, оскільки відчуття прохолоди, яке ви отримуєте від виклику chunkфункції в масиві, насправді не переважує додаткову складність, яку ви додаєте, і тонкі помилки, які можуть поплутатися із вбудованими прототипами.
Гордон Густафсон

12
Він не возиться з ними, він продовжує їх для масивів. Я розумію, що ніколи не торкаюся Object.prototype, тому що це буде міхур для всіх об'єктів (всього), але для цієї функції, визначеної для масиву, я не бачу жодних проблем.
rlemon

Упевнений , що має бути array.map(function(i)НЕ array.map(function(elem,i)хоча
Nigel Angel

3
На основі діаграми сумісності на сайті розробника mozilla, Array.map для IE9 +. Будь обережний.
Майкель Д

Будьте обережні, щоб передати плаваючий - 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 (індекс / perChunk)? Це середня?
me-me

5/2 = 2.5і Math.floor(2.5) = 2так предмет з індексом 5 буде розміщений у відрі 2
Андрій Р

Дякую Андрію Р. Ах, тож він проходить через 0 - 2. То як це називається в математичному плані? Я гадаю, що я ніколи не думав би ділити індекс / 2 на кожен індекс, щоб отримати індекс кожного фрагмента. Тому я намагаюся обернути голову навколо цього, тому що мені це дуже подобається, але не розумію його повністю в математичному плані. Я зазвичай роблю це для отримання середніх значень загальної кількості, але це виглядає інакше.
me-me

Це рішення є неефективним порівняно з іншими рішеннями, оскільки вам потрібно перебирати кожен елемент.
JP de la Torre

ви праві @JPdelaTorre, це, мабуть, не так ефективно, як рішення, які розрізають масиви, але ви розщеплюєте волоски тут. Більшість перерахованих відповідей було б неефективним за цим визначенням.
Андрій Р

102

Постарайтеся уникати спілкування з власними прототипами, включаючи Array.prototype, якщо ви не знаєте, хто буде споживати ваш код (треті сторони, співробітники, ви самі пізніше та інше).

Існують способи безпечного поширення прототипів (але не у всіх браузерах), і є способи безпечного споживання об'єктів, створених із розширених прототипів, але краще правило - дотримуватися Принципу найменшого сюрпризу та уникати цих практик взагалі.

Якщо у вас є деякий час, перегляньте виступ Ендрю Дюпона про 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;

25
"уникайте спілкування з натурними прототипами", нові розробники js повинні отримати тимчасову, якщо не постійну татуювання цієї фрази.
Джейкоб Далтон

2
Я використовую JavaScript протягом багатьох років і витрачаю поруч, не турбуючись з прототипом, в самих самих викликових функціях, ніколи не змінюючи, як ви бачите, як це роблять деякі люди.
1984 р.

2
найкраща пропозиція в моїх очах, найпростіша для розуміння та реалізації, велике спасибі!
Ольга Фарбер

1
@JacobDalton Я думаю, що в цьому всі вини університетів. Люди вважають, що OOP потрібно використовувати скрізь. Тож вони лякаються "просто створення функції". Вони хочуть бути впевненими, щоб помістити всередину щось. Навіть якщо це зовсім не підходить. Якщо немає нотації точок, немає "архітектури".
Герман

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

1
Дякуємо @AymKdn, що зробили цей показник: Це було дуже корисно! Я використовував підхід для сплайсингу, і він розбив мій браузер Chrome v70 розміром 884432. З запропонованим підходом "скибочка" мій код більше не завершує процес "візуалізації" браузера. :)
Бенні Нойгебауер

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

14
fyi, продуктивність цього методу дорівнює 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
Я відчуваю, що слід мати відмову в питаннях SO 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]]

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

Якщо є залишок, просто поверніть масив акумулятора.

Якщо немає залишку, то індекс ділиться на розмір шматка, тому візьміть фрагмент з початкового масиву (починаючи з поточного індексу) і додайте його до масиву акумулятора.

Отже, повернутий масив акумуляторів для кожної ітерації зменшення виглядає приблизно так:

// 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
мтс 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);

Не працює в iE8.
Nadeemmnn Mohd

4
ХА! я люблю кошеня коментар. вибачте за відсутність додаткових конструктивних матеріалів :)
29er

Я б це зробив (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 створюють непогані головоломки :-)


1
Я так само написав свою. Він все одно працює, якщо ви вилучите 0з fill, що робить fillпогляд трохи більш чутливим, імхо.
Коерт Гроббелаар

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;

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


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) видаляє підрив масиву [0..1] з a і повертає підмагістраль a [0..1]. Я роблю масив усіх цих масивів
Arpit Jain

2
Рекомендую замість splice () використовувати метод неруйнівного slice ()
Ash Blue

7
results = []
chunk_size = 10
while(array.length > 0){
   results.push(array.splice(0, chunk_size))
}

1
Не впевнений, чому це було знято з голосу, але код міг би використовувати якесь пояснення.
jpaugh

2
Оскільки сплайс руйнує початковий масив.
metalim

7

Наступний підхід ES2015 працює без необхідності визначення функції та безпосередньо на анонімних масивах (приклад з розміром 2):

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

Якщо ви хочете визначити функцію для цього, ви можете зробити це наступним чином (вдосконалюючи коментар K ._ щодо відповіді Блаземонгера ):

const array_chunks = (array, chunk_size) => array
    .map((_, i, all) => all.slice(i*chunk_size, (i+1)*chunk_size))
    .filter(x => x.length)

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



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

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

Використовуйте 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;
}

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


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


4

Ще одне рішення з використанням 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
}

2

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щоб змінити максимальну довжину менших масивів.


2

Ось немутуючий розчин, що використовує лише рекурсію та фрагмент ().

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/

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.