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

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

396 голосов
/ 14 декабря 2011

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

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

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

Ответы [ 46 ]

520 голосов
/ 14 декабря 2011

Метод 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
}
132 голосов
/ 05 мая 2012

Изменено из ответа 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));
}});
83 голосов
/ 01 августа 2012

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

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

Если у вас есть время, посмотрите хорошее выступление Эндрю Дюпона на конференции «onon 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;
68 голосов
/ 15 июня 2016

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

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
}, [])
34 голосов
/ 16 июля 2014

Я проверил разные ответы на 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;
}
29 голосов
/ 06 ноября 2012

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

var chunks = function(array, size) {
  var results = [];
  while (array.length) {
    results.push(array.splice(0, size));
  }
  return results;
};
26 голосов
/ 22 июня 2017

Однострочник в 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))
25 голосов
/ 24 июля 2012

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

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]]
23 голосов
/ 11 декабря 2015

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

13 голосов
/ 26 марта 2014

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

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