Скажем, у нас есть Map : let m = new Map();
, использование m.values()
возвращает итератор карты.
Но я не могу использовать forEach()
или map()
на этом итераторе, и реализация цикла while на этом итераторе кажется анти-шаблоном, поскольку ES6 предлагает такие функции, как map()
.
Так есть ли способ использовать map()
итератор?
lodash
map
функция, которая также поддерживает карту.Array.from(m.values()).map(...)
работает, но я думаю, что это не лучший способ сделать это.Array#map
?Ответы:
Самый простой и наименее эффективный способ сделать это:
Array.from(m).map(([key,value]) => /* whatever */)
Еще лучше
Array.from(m, ([key, value]) => /* whatever */))
Array.from
берет любую итеративную или похожую на массив вещь и преобразует ее в массив! Как указывает Даниэль в комментариях, мы можем добавить функцию сопоставления к преобразованию, чтобы удалить итерацию, а затем и промежуточный массив.Использование
Array.from
переместит вашу производительность сO(1)
на,O(n)
как отмечает @hraban в комментариях. Посколькуm
это aMap
, и они не могут быть бесконечными, нам не нужно беспокоиться о бесконечной последовательности. В большинстве случаев этого будет достаточно.Есть еще пара способов пролистать карту.
С помощью
forEach
m.forEach((value,key) => /* stuff */ )
С помощью
for..of
var myMap = new Map(); myMap.set(0, 'zero'); myMap.set(1, 'one'); for (var [key, value] of myMap) { console.log(key + ' = ' + value); } // 0 = zero // 1 = one
источник
Array.from(m, ([key,value]) => /* whatever */)
(обратите внимание, что функция сопоставления находится внутриfrom
), и тогда промежуточный массив не будет создан ( источник ). Он по-прежнему перемещается от O (1) к O (n), но по крайней мере итерация и отображение выполняются всего за одну полную итерацию.Вы можете определить другую функцию итератора, чтобы перебрать это:
function* generator() { for(let i = 0; i < 10; i++) { console.log(i); yield i; } } function* mapIterator(iterator, mapping) { while (true) { let result = iterator.next(); if (result.done) { break; } yield mapping(result.value); } } let values = generator(); let mapped = mapIterator(values, (i) => { let result = i*2; console.log(`x2 = ${result}`); return result; }); console.log('The values will be generated right now.'); console.log(Array.from(mapped).join(','));
Теперь вы можете спросить: почему бы просто не использовать
Array.from
вместо этого? Поскольку это будет проходить через весь итератор, сохраните его во (временном) массиве, повторите его снова и затем выполните сопоставление. Если список огромен (или даже потенциально бесконечен), это приведет к ненужному использованию памяти.Конечно, если список предметов невелик, использования
Array.from
должно быть более чем достаточно.источник
mapIterator()
не гарантирует, что базовый итератор будет правильно закрыт (iterator.return()
вызван), если только возвращаемое значение next не было вызвано хотя бы один раз. См .: Repeater.js.org/docs/safetyСамый простой и эффективный способ - использовать второй аргумент
Array.from
для достижения этой цели:const map = new Map() map.set('a', 1) map.set('b', 2) Array.from(map, ([key, value]) => `${key}:${value}`) // ['a:1', 'b:2']
Этот подход работает для любой , не бесконечно итерации. И это позволяет избежать использования отдельного вызова,
Array.from(map).map(...)
который будет повторять итерацию дважды и ухудшить производительность.источник
Вы можете получить итератор для итератора, а затем вернуть другой итератор, который вызывает функцию обратного вызова сопоставления для каждого повторяемого элемента.
const map = (iterable, callback) => { return { [Symbol.iterator]() { const iterator = iterable[Symbol.iterator](); return { next() { const r = iterator.next(); if (r.done) return r; else { return { value: callback(r.value), done: false, }; } } } } } }; // Arrays are iterable console.log(...map([0, 1, 2, 3, 4], (num) => 2 * num)); // 0 2 4 6 8
источник
Вы можете использовать itiriri, который реализует методы, подобные массивам, для итераций:
import { query } from 'itiriri'; let m = new Map(); // set map ... query(m).filter([k, v] => k < 10).forEach([k, v] => console.log(v)); let arr = query(m.values()).map(v => v * 10).toArray();
источник
Взгляните на https://www.npmjs.com/package/fluent-iterable
Работает со всеми итерациями (карта, функция генератора, массив) и асинхронными итерациями.
const map = new Map(); ... console.log(fluent(map).filter(..).map(..));
источник