Supposons que nous ayons une carte:, let m = new Map();
using m.values()
renvoie un itérateur de carte.
Mais je ne peux pas utiliser forEach()
ou map()
sur cet itérateur et implémenter une boucle while sur cet itérateur semble être un anti-pattern puisque ES6 offre des fonctions comme map()
.
Alors, y a-t-il un moyen d'utiliser map()
sur un itérateur?
lodash
map
fonction qui prend également en charge Map.Array.from(m.values()).map(...)
fonctionne, mais je pense que ce n'est pas la meilleure façon de le faire.Array#map
?Réponses:
Le moyen le plus simple et le moins performant de le faire est:
Array.from(m).map(([key,value]) => /* whatever */)
Mieux encore
Array.from(m, ([key, value]) => /* whatever */))
Array.from
prend n'importe quel élément itérable ou semblable à un tableau et le convertit en un tableau! Comme Daniel le souligne dans les commentaires, nous pouvons ajouter une fonction de mappage à la conversion pour supprimer une itération et par la suite un tableau intermédiaire.L'utilisation
Array.from
déplacera votre performance deO(1)
àO(n)
comme @hraban le souligne dans les commentaires. Puisquem
c'est aMap
, et qu'ils ne peuvent pas être infinis, nous n'avons pas à nous soucier d'une séquence infinie. Dans la plupart des cas, cela suffira.Il existe plusieurs autres façons de parcourir une carte.
En utilisant
forEach
m.forEach((value,key) => /* stuff */ )
En utilisant
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
la source
Array.from(m, ([key,value]) => /* whatever */)
(notez que la fonction de mappage est à l'intérieur defrom
) et aucun tableau intermédiaire n'est créé ( source ). Il passe toujours de O (1) à O (n), mais au moins l'itération et le mappage se produisent en une seule itération complète.Vous pouvez définir une autre fonction d'itérateur pour boucler sur ceci:
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(','));
Maintenant, vous pourriez demander: pourquoi ne pas simplement utiliser à la
Array.from
place? Étant donné que cela s'exécutera sur tout l'itérateur, enregistrez-le dans un tableau (temporaire), répétez-le, puis effectuez le mappage. Si la liste est énorme (voire potentiellement infinie), cela entraînera une utilisation inutile de la mémoire.Bien sûr, si la liste des éléments est assez petite, l'utilisation
Array.from
devrait être plus que suffisante.la source
mapIterator()
ne garantit pas que l'itérateur sous-jacent sera correctement fermé (iterator.return()
appelé) à moins que la valeur de retour suivante n'ait été appelée au moins une fois. Voir: repeater.js.org/docs/safetyCette méthode la plus simple et la plus performante consiste à utiliser le deuxième argument pour
Array.from
y parvenir:const map = new Map() map.set('a', 1) map.set('b', 2) Array.from(map, ([key, value]) => `${key}:${value}`) // ['a:1', 'b:2']
Cette approche fonctionne pour tout itérable non infini . Et cela évite d'avoir à utiliser un appel distinct
Array.from(map).map(...)
qui itérerait deux fois dans l'itérable et serait pire pour les performances.la source
Vous pouvez récupérer un itérateur sur l'itérable, puis renvoyer un autre itérateur qui appelle la fonction de rappel de mappage sur chaque élément itéré.
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
la source
Vous pouvez utiliser itiriri qui implémente des méthodes de type tableau pour les itérables:
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();
la source
Jetez un œil à https://www.npmjs.com/package/fluent-iterable
Fonctionne avec tous les itérables (Map, fonction de générateur, tableau) et les itérables asynchrones.
const map = new Map(); ... console.log(fluent(map).filter(..).map(..));
la source