Powiedzmy, że mamy Map : let m = new Map();
, użycie m.values()
zwraca iterator mapy.
Ale nie mogę używać forEach()
lub map()
na tym iteratorze, a implementacja pętli while na tym iteratorze wydaje się być anty-wzorcem, ponieważ ES6 oferuje funkcje takie jak map()
.
Czy jest więc sposób użycia map()
na iteratorze?
javascript
dictionary
syntax
ecmascript-6
iterator
shinzou
źródło
źródło
lodash
map
funkcja, która obsługuje również Mapę.Array.from(m.values()).map(...)
działa jak działa, ale myślę, że nie jest to najlepszy sposób na zrobienie tego.Array#map
?Odpowiedzi:
Najprostszym i najmniej wydajnych sposobem, aby to zrobić, to:
Array.from(m).map(([key,value]) => /* whatever */)
Jeszcze lepiej
Array.from(m, ([key, value]) => /* whatever */))
Array.from
pobiera dowolną iterowalną lub podobną do tablicy rzecz i konwertuje ją na tablicę! Jak Daniel wskazuje w komentarzach, możemy dodać funkcję odwzorowującą do konwersji, aby usunąć iterację, a następnie tablicę pośrednią.Użycie
Array.from
spowoduje przeniesienie wydajności zO(1)
do,O(n)
jak wskazuje @hraban w komentarzach. Ponieważm
jest aMap
i nie mogą być nieskończone, nie musimy się martwić o nieskończoną sekwencję. W większości przypadków to wystarczy.Istnieje kilka innych sposobów przeglądania mapy.
Za pomocą
forEach
m.forEach((value,key) => /* stuff */ )
Za pomocą
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
źródło
Array.from(m, ([key,value]) => /* whatever */)
(zauważ, że funkcja mapowania jest wewnątrzfrom
), a następnie nie zostanie utworzona żadna pośrednia tablica ( źródło ). Nadal porusza się od O (1) do O (n), ale przynajmniej iteracja i mapowanie odbywa się tylko w jednej pełnej iteracji.Możesz zdefiniować inną funkcję iteratora, aby zapętlić to:
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(','));
Teraz możesz zapytać: dlaczego po prostu nie użyć
Array.from
zamiast tego? Ponieważ będzie to przebiegać przez cały iterator, zapisz go w (tymczasowej) tablicy, wykonaj iterację ponownie, a następnie wykonaj mapowanie. Jeśli lista jest ogromna (lub nawet potencjalnie nieskończona), doprowadzi to do niepotrzebnego zużycia pamięci.Oczywiście, jeśli lista pozycji jest dość mała, użycie
Array.from
powinno być więcej niż wystarczające.źródło
mapIterator()
nie gwarantuje, że bazowy iterator zostanie poprawnie zamknięty (iterator.return()
wywołany), chyba że następna wartość zwracana została wywołana przynajmniej raz. Zobacz: repeater.js.org/docs/safetyTen najprostszy i najbardziej wydajny sposób polega na użyciu drugiego argumentu, aby
Array.from
to osiągnąć:const map = new Map() map.set('a', 1) map.set('b', 2) Array.from(map, ([key, value]) => `${key}:${value}`) // ['a:1', 'b:2']
To podejście działa dla każdej nieskończonej iteracji. I unika konieczności używania oddzielnego wywołania,
Array.from(map).map(...)
które mogłoby dwukrotnie iterować przez iterowalne i pogorszyć wydajność.źródło
Możesz pobrać iterator po iterowalnym, a następnie zwrócić inny iterator, który wywołuje funkcję wywołania zwrotnego odwzorowania na każdym iterowanym elemencie.
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
źródło
Możesz użyć itiriri, który implementuje metody podobne do tablic dla iterable:
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();
źródło
Spójrz na https://www.npmjs.com/package/fluent-iterable
Działa ze wszystkimi iteracjami (mapa, funkcja generatora, tablica) i asynchronicznymi.
const map = new Map(); ... console.log(fluent(map).filter(..).map(..));
źródło