Disons que nous avons une carte: let m = new Map();
, en utilisant 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 ressemble à un anti-motif puisque les fonctions offertes par ES6 sont telles que map()
.
Alors, y a-t-il moyen d'utiliser map()
sur un itérateur?
La façon la plus simple et la moins performante est la suivante:
_Array.from(m).map(([key,value]) => /* whatever */)
_
Mieux encore
_Array.from(m, ([key, value]) => /* whatever */))
_
Array.from
prend n'importe quelle chose itérable ou semblable à un tableau et le convertit en 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.
Utiliser _Array.from
_ fera passer votre performance de O(1)
à O(n)
, comme le fait remarquer @hraban dans les commentaires. Puisque m
est un Map
, 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 deux autres moyens de parcourir en boucle une carte.
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
_
Vous pouvez définir une autre fonction d'itérateur à 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 Array.from
à la place? Parce que cela va parcourir l'itérateur en entier, enregistrez-le dans un tableau (temporaire), itérez-le à nouveau et then faites le mappage. Si la liste est longue (voire potentiellement infinie), cela entraînera une utilisation inutile de la mémoire.
Bien sûr, si la liste d'éléments est assez petite, utiliser Array.from
devrait être plus que suffisant.
Cette méthode la plus simple et la plus performante consiste à utiliser le deuxième argument de Array.from
pour obtenir ceci:
_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 non-infini itérable. Et cela évite d’avoir à utiliser un appel séparé à Array.from(map).map(...)
qui parcourrait deux fois l’itéré et serait pire pour la performance.
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
Vous pouvez utiliser itiriri qui implémente des méthodes de type tableau pour les iterables:
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();