Разделить массив на куски

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

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

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

Ответ 1

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

Ответ 2

Изменено в ответе dbaseman: fooobar.com/questions/27185/...

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

Ответ 3

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

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

Если у вас есть время, просмотрите доклад Andrew 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;

Ответ 4

Вот версия 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']]

Ответ 5

Я проверил разные ответы на 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;
}

Ответ 6

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

var chunks = function(array, size) {
  var results = [];
  while (array.length) {
    results.push(array.splice(0, size));
  }
  return results;
};

Ответ 7

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

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

Ответ 8

Один вкладыш в 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

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

Ответ 10

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

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 к числу, мы можем сделать ручное currying следующим образом:

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

Ответ 11

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

Ответ 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));

Ответ 13

Если вы используете EcmaScript version> = 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' ] ]

Ответ 14

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

Ответ 15

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

Ответ 16

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

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

Ответ 17

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

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

Ответ 18

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

Ответ 19

И это был бы мой вклад в эту тему. Я думаю, .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));

Ответ 20

Однострочный подход 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]]

Ответ 21

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

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

Ответ 22

ES6 Generator версия

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

Ответ 23

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

Ответ 24

Следующий подход 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)

Ответ 25

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, чтобы изменить максимальную длину меньших массивов.

Ответ 26

Вот не мутирующее решение, использующее только рекурсию и срез().

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/

Ответ 27

Я немного изменил 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);
}

Ответ 28

Я создал следующий 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 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;
  }
}

Ответ 29

Я просто написал это с помощью функции 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));

Ответ 30

Здесь мой подход, используя понимание списка Coffeescript. Замечательная статья, в которой подробно описывается понимание в Coffeescript, может быть здесь.

chunk: (arr, size) ->
    chunks = (arr.slice(index, index+size) for item, index in arr by size)
    return chunks