Angenommen, wir haben eine Karte: let m = new Map();
Mit wird m.values()
ein Karteniterator zurückgegeben.
Aber ich kann forEach()
oder map()
auf diesem Iterator nicht verwenden und die Implementierung einer while-Schleife auf diesem Iterator scheint ein Anti-Pattern zu sein, da ES6 Funktionen wie bietet map()
.
Gibt es also eine Möglichkeit, map()
einen Iterator zu verwenden?
javascript
dictionary
syntax
ecmascript-6
iterator
Shinzou
quelle
quelle
lodash
map
Funktionen von Drittanbietern verwenden, die auch Map unterstützen.Array.from(m.values()).map(...)
funktioniert wie folgt, aber ich denke, dies ist nicht der beste Weg, dies zu tun.Array#map
?Antworten:
Der einfachste und am wenigsten performante Weg, dies zu tun, ist:
Array.from(m).map(([key,value]) => /* whatever */)
Besser noch
Array.from(m, ([key, value]) => /* whatever */))
Array.from
nimmt jedes iterierbare oder Array-ähnliche Objekt und konvertiert es in ein Array! Wie Daniel in den Kommentaren hervorhebt, können wir der Konvertierung eine Zuordnungsfunktion hinzufügen, um eine Iteration und anschließend ein Zwischenarray zu entfernen.Mit
Array.from
wird Ihre Leistung vonO(1)
nachO(n)
verschoben , wie @hraban in den Kommentaren hervorhebt. Dam
aMap
ist und sie nicht unendlich sein können, müssen wir uns keine Sorgen um eine unendliche Folge machen. In den meisten Fällen reicht dies aus.Es gibt noch einige andere Möglichkeiten, eine Karte zu durchlaufen.
Verwenden von
forEach
m.forEach((value,key) => /* stuff */ )
Verwenden von
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
quelle
Array.from(m, ([key,value]) => /* whatever */)
(beachten Sie, dass sich die Zuordnungsfunktion innerhalb von befindetfrom
) und dann kein Zwischenarray erstellt wird ( Quelle ). Es bewegt sich immer noch von O (1) nach O (n), aber mindestens Iteration und Mapping erfolgen in nur einer vollständigen Iteration.Sie können eine andere Iteratorfunktion definieren, um diese zu durchlaufen:
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(','));
Jetzt könnten Sie fragen: Warum nicht einfach
Array.from
stattdessen verwenden? Da dies den gesamten Iterator durchläuft, speichern Sie ihn in einem (temporären) Array, iterieren Sie ihn erneut und führen Sie dann die Zuordnung durch. Wenn die Liste riesig (oder sogar unendlich) ist, führt dies zu einer unnötigen Speichernutzung.Wenn die Liste der Elemente relativ klein ist,
Array.from
sollte die Verwendung natürlich mehr als ausreichend sein.quelle
mapIterator()
garantiert nicht, dass der zugrunde liegende Iterator ordnungsgemäß geschlossen (iterator.return()
aufgerufen) wird, es sei denn, der nächste Rückgabewert wurde mindestens einmal aufgerufen. Siehe: repeater.js.org/docs/safetyDieser einfachste und performanteste Weg besteht darin, das zweite Argument zu verwenden
Array.from
, um dies zu erreichen:const map = new Map() map.set('a', 1) map.set('b', 2) Array.from(map, ([key, value]) => `${key}:${value}`) // ['a:1', 'b:2']
Dieser Ansatz funktioniert für alle nicht unendlich iterierbaren. Außerdem wird vermieden, dass ein separater Aufruf verwendet werden muss,
Array.from(map).map(...)
der die Iterable zweimal durchläuft und die Leistung beeinträchtigt.quelle
Sie können einen Iterator über die Iterable abrufen und dann einen anderen Iterator zurückgeben, der die Zuordnungsrückruffunktion für jedes iterierte Element aufruft.
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
quelle
Sie können itiriri verwenden , das Array-ähnliche Methoden für Iterables implementiert:
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();
quelle
Schauen Sie sich https://www.npmjs.com/package/fluent-iterable an
Funktioniert mit allen iterablen (Map, Generatorfunktion, Array) und asynchronen iterables.
const map = new Map(); ... console.log(fluent(map).filter(..).map(..));
quelle