ES5/6/Следующий способ перебора групп или фрагментов массива в JavaScript

Допустим, у меня есть массив, и я хочу перебирать их по N за раз. Я могу использовать простой вложенный цикл

const array = ['a','b','c','d','e','f','g','h'];
const step = 3;
for (let i = 0; i < array.length; i += step) {
   const end = Math.min(i + step, array.length);
   console.log("page:", i / step);
   for (let j = i; j < end; ++j) {
     const element = array[j];
     console.log("  ", element);
   }
}

выходы:

page: 0
   a
   b
   c
page: 1
   d
   e
   f
page: 2
   g
   h

Но мне интересно, есть ли более современный способ, например forEach, в JavaScript в наши дни. Представлять себе:

// pseudo code
const chunkSize = 3
forEachByChunk(array, chunkSize, (subArray, chunkNdx, startingNdxInParentArray) => {
  console.log("page:", chunkNdx);
  subArray.forEach((element) => { console.log("  ", element); });
});

Примечание: я могу написать свой собственный forEachByChunk

function forEachByChunk(array, chunkSize, fn) {
  const end = array.length;
  for (let i = 0; i < end; i += chunkSize) {
    fn(array.slice(i, i + chunkSize), i / chunkSize, i);
  }
}

Мне просто интересно, нет ли уже встроенного современного способа сделать это в JavaScript. Или, может быть, более универсальная версия.


person gman    schedule 27.08.2017    source источник
comment
Нет, практически все встроенные функции будут перебирать весь массив, не пропуская индексы. Вы можете использовать условие внутри обратного вызова, но более эффективно просто использовать цикл for, как в первом примере. Добавление к нативным прототипам, как правило, тоже не очень хорошая идея.   -  person adeneo    schedule 27.08.2017
comment
Именно по этой причине я реализовал array.stride. Ищите его в npm. Поскольку это самореклама, я не буду записывать это как ответ, если не попросят.   -  person slebetman    schedule 27.08.2017
comment
Кроме того, я надеялся предложить array.stride для ES8, но не нашел времени написать предложение.   -  person slebetman    schedule 27.08.2017
comment
@slebetman Нет веской причины иметь собственный итератор фрагментов по двум причинам. Во-первых, легко построить итератор фрагмента из итератора элемента. Во-вторых, это не дает никаких преимуществ в производительности, потому что вам все равно придется перебирать каждый элемент в чанке, чтобы скопировать весь чанк. Например, если вы используете slice для создания чанка, вы перебираете каждый элемент чанка, чтобы все равно его скопировать.   -  person Aadit M Shah    schedule 27.08.2017
comment
@AaditMShah: всегда есть веская причина для ясности кода. Точно так же, как метод .bind() функций. По вашим рассуждениям нет веской причины иметь это на языке, потому что вы можете его реализовать. Для итератора фрагментов вы определенно можете повысить производительность, реализовав его на C.   -  person slebetman    schedule 27.08.2017
comment
@slebetman Я не согласен. Во-первых, если вас беспокоит ясность кода, то что плохого в определении собственной функции stride? Тот факт, что JavaScript предоставляет bind, не означает, что он должен предоставлять все удобные функции. Во-вторых, не нужно беспокоиться о производительности, потому что современные движки JavaScript уже сильно оптимизируют циклы. Независимо от того, реализуете ли вы итератор фрагментов в JavaScript или в C, производительность будет относительно одинаковой. В-третьих, как правило, производительность итератора фрагментов никогда не является узким местом в любом алгоритме. Преждевременная оптимизация?   -  person Aadit M Shah    schedule 27.08.2017
comment
@AaditMShah Меня не беспокоила производительность, вы упомянули об этом, я просто отвечал на ваш комментарий о производительности. Как есть, операции с массивами не завершены. Здесь нет функции шага и функции слияния (другие языки иногда называют их unzip и zip). Моя реализация stride решает проблему отсутствия распаковки. У меня есть другая реализация Array.and, которая пытается решить проблему отсутствия zip. ИМХО, моя реализация stride лучше, чем реализация zip на других языках, а foreach в tcl является единственной лучшей реализацией.   -  person slebetman    schedule 27.08.2017
comment
@AaditMShah: Кроме того, на мой взгляд, определение собственной функции шага совершенно нормально. Так же, как для jQuery, Dojo, lodash и прототипа было совершенно нормально определять свою собственную функцию foreach много лет назад, когда в javascript не было Array.prototype.foreach. Но мы можем узнать, чего не хватает в стандартной библиотеке языка, и улучшить ее, чтобы значительно улучшить повторное использование кода.   -  person slebetman    schedule 27.08.2017
comment
Ваш вопрос неоднозначен. Вы хотите более современный способ или встроенный современный способ?   -  person    schedule 28.08.2017


Ответы (3)


Мне просто интересно, нет ли уже встроенного современного способа сделать это в JavaScript.

Нет, нет.

Или, может быть, более универсальная версия.

Существует множество пользовательских версий. Google для «массива разделов javascript». Вот кое-что для начала.

person Community    schedule 27.08.2017

Проверьте чанк из lodash. Он делает именно то, что вы хотите.

Проверьте lodash и rambda в целом. У них есть почти любой общий вспомогательный метод, о котором вы только можете подумать.

_.chunk([1, 2, 3, 4, 5, 6, 7, 8], 3) возвращает [[1, 2, 3], [4, 5, 6], [7, 8]]

Редактировать: Если вы ищете, как такую ​​вещь можно реализовать с использованием нового синтаксиса, вот как вы можете это сделать.

const chunk = size => list =>
  list
    .map((value, index) => ({ value, step: index % size }))
    .reduce(
      (accum, item) => {
        const { value, step } = item;
        if (step === 0) return [[value], ...accum];
        const [head, ...tail] = accum;
        return [[...head, value], ...tail];
      },
      [],
    ).slice().reverse();

Я надеюсь, что вы не используете это все же. Это не проверено, вам, вероятно, намного лучше с lodash.

person jaihindhreddy    schedule 27.08.2017
comment
ОП ищет встроенный итератор фрагментов. Следовательно, использование lodash — это большое нет-нет. - person Aadit M Shah; 27.08.2017

использование фрагмента и карты может решить проблему

const step = 3;
const array = ['a','b','c','d','e','f','g','h'];
var pages = array.map( (letter,index)=> index%step===0 ? array.slice(index,index+step) : [] )
.filter((page)=>page.length>=0);

person Zhang Bruce    schedule 27.08.2017
comment
Но ОП, похоже, ищет встроенный итератор фрагментов. Или более универсальная версия. Из которых это ни. - person ; 27.08.2017
comment
сращивание и карта построены по общему методу, я не понимаю, 2 голоса против этого и 2 голоса за ответ, в котором говорится, что нет - person Zhang Bruce; 27.08.2017
comment
Вопрос в том, есть ли встроенный метод разметки. Правильный ответ - нет, нет. Если ОП искал самодельный пользовательский алгоритм для разбиения, есть сотни доступных через простой поиск, которые, откровенно говоря, лучше, чем ваш. - person ; 28.08.2017