объединение двух объектов из массива объектов [duplicate]

Я просто столкнулся с проблемой нарезки и тут же приземлился. Итак, позвольте мне добавить мои два цента к этому.

Приведем пример из «производственного кода» (или что-то вроде близкого):


Допустим, у нас есть то, что рассылает действия. Например, пользовательский интерфейс центра управления. Этот пользовательский интерфейс должен получить список вещей, которые в настоящее время могут быть отправлены. Поэтому мы определяем класс, содержащий информацию о доставке. Назовем это Action. Таким образом, Action имеет некоторые переменные-члены. Для простоты мы имеем только 2, являющиеся std::string name и a std::function<void()> f. Затем он имеет void activate(), который просто выполняет член f.

Таким образом, пользовательский интерфейс получает std::vector<Action>. Представьте себе некоторые функции, такие как:

void push_back(Action toAdd);

Теперь мы установили, как это выглядит с точки зрения пользовательского интерфейса. Пока нет проблем. Но какой-то другой парень, который работает над этим проектом, внезапно решает, что существуют специальные действия, требующие дополнительной информации в объекте Action. По какой причине. Это также можно решить с помощью лямбда-захватов. Этот пример не принимается 1-1 из кода.

Итак, парень происходит от Action, чтобы добавить свой собственный вкус. Он передает экземпляр своего урожая в класс push_back, но затем программа переходит в haywire.

Итак, что случилось? Как вы могли предположить : объект был отрезан.

Дополнительная информация из экземпляра потеряна, а f теперь подвержен неопределенному поведению.

]

Надеюсь, что в этом примере рассказывается о тех людях, которые не могут себе представить, когда говорят о A s и B в какой-то мере.

332
задан Thaddeus Albers 11 September 2013 в 01:02
поделиться

32 ответа

Метод 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
}
403
ответ дан Blazemonger 21 August 2018 в 23:36
поделиться
  • 1
    Помните, если это функция утилиты, чтобы утверждать, что chunk является 0. (бесконечная петля) – Steven Lu 25 January 2014 в 02:51
  • 2
    Нет, последний кусок должен быть меньше остальных. – Blazemonger 23 July 2014 в 00:27
  • 3
    @Blazemonger, действительно! В следующий раз я попробую это сделать, прежде чем перейти к выводам. Я предположил (неправильно), что передача ввода в array.slice, которая превысила границы массива, была бы проблемой, но она работает просто отлично! – rysqui 23 July 2014 в 19:48
  • 4
    Для однострочных (любителей цепочки) : 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));. – K._ 4 February 2018 в 13:39
  • 5
    @Alex Это микро-оптимизация, кэширование значения j (что может повысить производительность, если j достаточно велико). В вашем коде array.length повторно вычисляется на каждой итерации (или, по крайней мере, когда я написал этот ответ, я уверен, что некоторые JS-движки улучшились с тех пор). – Blazemonger 16 March 2018 в 17:02

Я создал следующий 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), мы переходим к функции splitArray.

Функция splitArray принимает разделитель (это означает 8, так как это то, что вы хотите разделить), и сам массив. Поскольку мы многократно используем длину массива, я кэширую его в переменной, а также first и last.

first представляет позицию первого элемента в массив. Этот массив представляет собой массив из 8 элементов. Поэтому для определения первого элемента мы используем оператор модуля .

myArrays - это массив массивов. В нем мы будем хранить в каждом индексе любой подвальный массив размером 8 или ниже. Это ключевая стратегия в приведенном ниже алгоритме.

index представляет индекс для переменной myArrays. Каждый раз, когда необходимо сохранить вспомогательный массив из 8 элементов или меньше, его необходимо сохранить в соответствующем индексе. Итак, если у нас есть 27 элементов, это означает 4 массива. Первый, второй и третий массивы будут содержать по 8 элементов. У последнего будет только 3 элемента. Таким образом, index будет равен 0, 1, 2 и 3.

Сложная часть просто вычисляет математику и оптимизирует ее как можно лучше. Например, else if (delimiter - (i % delimiter) === 1) это найти последний элемент, который должен идти в массиве, когда массив будет заполнен (пример: содержать 10 элементов).

Этот код работает для каждого отдельного сценария, вы можете даже измените значение delimiter в соответствии с размером любого размера, который вы хотите получить. Довольно сладкое право: -)

Есть вопросы? Не стесняйтесь спрашивать в комментариях ниже.

1
ответ дан AGE 21 August 2018 в 23:36
поделиться

Вот версия 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
}, [])
38
ответ дан Andrei R 21 August 2018 в 23:36
поделиться
  • 1
    Это похоже на самое сжатое решение. Что такое chunkIndex = Math.floor (index / perChunk)? Это средняя? – me-me 5 June 2018 в 03:32
  • 2
    5/2 = 2.5 и Math.floor(2.5) = 2, поэтому элемент с индексом 5 будет помещен в ведро 2 – Andrei R 7 June 2018 в 03:34
  • 3
    Спасибо Андрею Р. Ах, так он проходит через 0 - 2. Так что же это называется в математике? Думаю, я хочу сказать, что никогда бы не подумал разделить индекс / 2 на каждый индекс, чтобы получить индекс каждого среза. Поэтому я пытаюсь обвести вокруг себя, потому что мне это очень нравится, но я не понимаю его полностью в математике. Обычно я делаю это, чтобы получить среднее число от общего числа, но это выглядит по-другому. – me-me 7 June 2018 в 13:35

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

var chunks = function(array, size) {
  var results = [];
  while (array.length) {
    results.push(array.splice(0, size));
  }
  return results;
};
24
ответ дан Arek Flinik 21 August 2018 в 23:36
поделиться
  • 1
    Будьте осторожны с этим решением для сращивания, так как он изменяет исходный массив, поэтому обязательно четко документируйте побочные эффекты. – bdrx 13 May 2014 в 17:26
  • 2
    Затем используйте срез – mplungjan 24 April 2015 в 21:29
  • 3
    @mplungjan Результат будет одним и тем же массивом снова и снова при использовании среза. Таким образом, на самом деле это не замена, без каких-либо дополнительных изменений. – nietonfir 2 January 2016 в 01:23
  • 4
    Единственное, что я хотел бы добавить к этому ответу, это clone в исходный массив. Я бы сделал это с помощью оператора распространения ES6. var clone = [...array], затем выполните проверку длины и сращивание над клонированным массивом. – MauricioLeal 1 November 2017 в 17:35
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 ] ]
6
ответ дан Arpit Jain 21 August 2018 в 23:36
поделиться
  • 1
    Расскажите немного о своем коде. – Sulthan Allaudeen 11 April 2014 в 11:12
  • 2
    a.splice (0, 2) удаляет подмассив из [0..1] из a и возвращает подмассиво a [0..1]. Я создаю массив из всех этих массивов – Arpit Jain 15 April 2014 в 20:19
  • 3
    Я рекомендую использовать метод неразрушающего slice () вместо splice () – Ash Blue 27 August 2014 в 21:15

Я тестировал разные ответы на 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;
}
19
ответ дан AymKdn 21 August 2018 в 23:36
поделиться
  • 1
    Спасибо @AymKdn за то, что вы сделали этот тест: это было так полезно! Я использовал подход к сращиванию , и он разбил мой браузер Chrome v70 с размером блока 884432. С вашим предлагаемым «срезом», подход на месте, мой код не разбивает «рендеринг». процесс браузера. :) – Benny Neugebauer 9 August 2018 в 13:57

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

1
ответ дан Chris Martin 21 August 2018 в 23:36
поделиться

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

Демонстрация:

> [1,2,3,4,5,6,7].chunk_inefficient(3)
[[1,2,3],[4,5,6],[7]]
< hr>

второстепенное дополнение :

Следует отметить, что вышеизложенное - это не очень элегантное (на мой взгляд) обходное решение для использования Array.map. В основном это означает, что ~ является конкатенацией:

[[1,2,3]]~[]~[]~[] ~ [[4,5,6]]~[]~[]~[] ~ [[7]]

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

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

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

  }
});

Демонстрация:

> 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));
}});
124
ответ дан Community 21 August 2018 в 23:36
поделиться
  • 1
    Эх, я бы избегал возиться с прототипом, поскольку ощущение прохлады, которое вы получаете от вызова функции chunk в массиве, на самом деле не перевешивает дополнительную сложность, которую вы добавляете, и тонкие ошибки, которые возились со встроенными прототипами может вызвать. – Gordon Gustafson 4 July 2012 в 02:19
  • 2
    Он не возится с ними, он расширяет их для Массивов. Я понимаю, что никогда не касаюсь Object.prototype, потому что это будет пузыриться ко всем объектам (все), но для этой функции Array я не вижу никаких проблем. – rlemon 24 July 2012 в 20:45
  • 3
    Довольно уверен, что это должно быть array.map(function(i) не array.map(function(elem,i), хотя – Nigel Angel 14 March 2013 в 19:19
  • 4
    На основе диаграммы совместимости на сайте разработчика mozilla, Array.map для IE9 +. Быть осторожен. – Maikel D 4 June 2013 в 07:13
  • 5
    Будьте осторожны, чтобы пройти плавающий тип числа 7,5 - привести к непредсказуемым кускам – Gal Bracha 20 January 2015 в 00:25

Это простое решение, использующее решение @Blazemonger

function array_chunk(arr, size){
    // initialize vars
    var i,
    j = arr.length,
    tempArray = [];
    // loop through and jump based on size
    for (i=0; i<j; i+=size) {
        // slice chunk of arr and push to tempArray
        tempArray.push(arr.slice(i,i+size));
    }
    // return temp array (chunck)
    return tempArray
}

. Это привело к тому, что у меня получился поток, надеюсь, что это поможет кому-то другому. :)

0
ответ дан Daniel Barde 21 August 2018 в 23:36
поделиться

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

1
ответ дан dentaku 21 August 2018 в 23:36
поделиться

Однострочный подход 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
ответ дан dhilt 21 August 2018 в 23:36
поделиться

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

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

Если у вас есть время, просмотрите доклад Андрея Дюпона в 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;
65
ответ дан furf 21 August 2018 в 23:36
поделиться
  • 1
    «избегать смещения с родными прототипами» новые разработчики js должны получить временную, если не постоянную, татуировку этой фразы. – Jacob Dalton 15 February 2017 в 23:04
  • 2
    Я использую javascript в течение многих лет и трачу время на то, чтобы не беспокоиться о прототипе, при самых вызывающих функциях, никогда не изменяя, как вы видите, некоторые люди делают. – 1984 22 June 2017 в 19:54
  • 3
    лучшее предложение в моих глазах, самое простое в понимании и в реализации, большое спасибо! – Olga Farber 15 May 2018 в 11:45

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

18
ответ дан George Herolyants 21 August 2018 в 23:36
поделиться
  • 1
    Я чувствую, что должен быть отказ от SO javascript вопросов: вы пробовали lodash? В значительной степени первое, что я включаю в узел или в браузер. – Jacob Dalton 5 April 2016 в 22:38

Если вы используете версию 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 items
  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
ответ дан matsev 21 August 2018 в 23:36
поделиться
results = []
chunk_size = 10
while(array.length > 0){
   results.push(array.splice(0, chunk_size))
}
4
ответ дан Mihai Iorga 21 August 2018 в 23:36
поделиться
  • 1
    Не уверен, почему это было отклонено, но код может использовать некоторые объяснения. – jpaugh 19 April 2017 в 15:00
  • 2
    Поскольку сплайсинг разрушает исходный массив. – metalim 26 December 2017 в 20:10

Здесь аккуратный & amp; оптимизированная реализация функции chunk(). Предполагая размер блока по умолчанию 10.

var chunk = function(list, chunkSize) {
  if (!list.length) {
    return [];
  }
  if (typeof chunkSize === undefined) {
    chunkSize = 10;
  }

  var i, j, t, chunks = [];
  for (i = 0, j = list.length; i < j; i += chunkSize) {
    t = list.slice(i, i + chunkSize);
    chunks.push(t);
  }

  return chunks;
};

//calling function
var list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
var chunks = chunk(list);
1
ответ дан Mohan Dere 21 August 2018 в 23:36
поделиться

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

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;
}
2
ответ дан mpen 21 August 2018 в 23:36
поделиться

Я немного изменил 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);
}
1
ответ дан nevace 21 August 2018 в 23:36
поделиться
# 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 ' '
0
ответ дан Patrick Chu 21 August 2018 в 23:36
поделиться

Вот мой подход с использованием понимания списка Coffeescript. Огромная статья, в которой подробно описываются соображения в Coffeescript, может быть найдена здесь .

chunk: (arr, size) ->
    chunks = (arr.slice(index, index+size) for item, index in arr by size)
    return chunks
1
ответ дан pymarco 21 August 2018 в 23:36
поделиться

И это будет моим вкладом в эту тему. Я думаю, что .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}).map((_,i) => i+1);
res = chunk(arr,7);
console.log(JSON.stringify(res));

3
ответ дан Redu 21 August 2018 в 23:36
поделиться

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

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]]
22
ответ дан rlemon 21 August 2018 в 23:36
поделиться
  • 1
    fyi, производительность этого метода O (N ^ 2), поэтому его нельзя использовать в критичных для производительности разделах кода или с длинными массивами (в частности, когда размер .length массива намного больше, чем размер блока n). Если бы это был ленивый язык (в отличие от javascript), этот алгоритм не страдал бы от O (N ^ 2) времени. Тем не менее, рекурсивная реализация элегантна. Возможно, вы можете изменить его, чтобы улучшить производительность, сначала определяя вспомогательную функцию, которая рекурсивно работает на array,position, а затем отправляет: Array.prototype.chunk возвращает [вашу вспомогательную функцию] (...) – ninjagecko 23 August 2012 в 02:52
  • 2
    благодаря сенсаю, чтобы благословить меня своим духом, который я должен был исполнить сегодня вечером – bitten 11 October 2016 в 20:25
  • 3
    или ... var chunk = (arr, n) => { if ( !arr.length ) return []; return [ arr.slice( 0, n ) ].concat( chunk(arr.slice(n), n) ) } – Ed Williams 13 December 2017 в 10:13

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);
2
ответ дан Rm558 21 August 2018 в 23:36
поделиться

Однострочный в 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))
13
ответ дан Shairon Toledo 21 August 2018 в 23:36
поделиться

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

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]]
2
ответ дан Steve Holgado 21 August 2018 в 23:36
поделиться

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

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/

2
ответ дан Suhair Zain 21 August 2018 в 23:36
поделиться

Вот решение, использующее ImmutableJS, где items - это неизменяемый список, а size - необходимый размер группировки.

const partition = ((items, size) => {
  return items.groupBy((items, i) => Math.floor(i/size))
})
0
ответ дан taylor 21 August 2018 в 23:36
поделиться

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

8
ответ дан thoredge 21 August 2018 в 23:36
поделиться
  • 1
    Очень элегантное решение! – The F 8 November 2017 в 23:57
  • 2
    Я тоже так писал. Он по-прежнему работает, если вы удалите 0 из fill, что делает fill выглядеть немного более разумным, imho. – Coert Grobbelaar 31 July 2018 в 21:32

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

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);
10
ответ дан user239558 21 August 2018 в 23:36
поделиться
  • 1
    Не работает в iE8. – Nadeemmnn Mohd 8 June 2016 в 11:30
  • 2
    ХА! Мне нравится комментарий котенка. извините за отсутствие дополнительного конструктивного ввода :) – 29er 5 April 2017 в 17:26
  • 3

Создал пакет 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;
5
ответ дан zhiyelee 21 August 2018 в 23:36
поделиться
Другие вопросы по тегам:

Похожие вопросы: