151 votes

Utilisation de map() sur un itérateur

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() o map() sur cet itérateur et l'implémentation d'une boucle while sur cet itérateur semble être un anti-modèle puisque l'ES6 offre des fonctions telles que map() .

Y a-t-il un moyen d'utiliser map() sur un itérateur ?

0 votes

Pas dans la boîte, mais vous pouvez utiliser des bibliothèques tierces telles que lodash map qui prend également en charge la fonction Map.

0 votes

La carte elle-même possède un forEach pour itérer sur ses paires clé-valeur.

0 votes

Convertir l'itérateur en tableau et le mapper comme suit Array.from(m.values()).map(...) fonctionne, mais je pense que ce n'est pas la meilleure façon de faire.

131voto

ktilcu Points 26

El le plus simple y le moins performant La façon de faire est la suivante :

Array.from(m).map(([key,value]) => /* whatever */)

Mieux encore

Array.from(m, ([key, value]) => /* whatever */))

Array.from prend n'importe quel itérable ou objet similaire à un tableau et le convertit en un tableau ! Comme Daniel le fait remarquer 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.

Utilisation de Array.from fera passer vos performances de O(1) a O(n) comme le souligne @hraban dans les commentaires. Depuis m est un Map et 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 boucle.

Utilisation de forEach

m.forEach((value,key) => /* stuff */ )

Utilisation de 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

0 votes

Les cartes peuvent-elles avoir une longueur infinie ?

2 votes

@ktilcu pour un itérateur : oui. un .map sur un itérateur peut être considéré comme une transformation sur le générateur, qui renvoie un itérateur lui-même. le popping d'un élément appelle l'itérateur sous-jacent, transforme l'élément et renvoie le tout.

0 votes

Je pense que le Array.from La partie garantit l'ensemble fini. Je comprends comment un générateur pourrait fournir une séquence paresseuse et infinie, mais cela ne fait pas partie de cette question.

25voto

sheean Points 131

Vous pourriez définir une autre fonction d'itération pour boucler sur cette fonction :

function* generator() {
    for (let i = 0; i < 10; i++) {
        console.log(i);
        yield i;
    }
}

function* mapIterator(iterator, mapping) {
    for (let i of iterator) {
        yield mapping(i);
    }
}

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 vous demander : pourquoi ne pas simplement utiliser Array.from à la place ? Parce que cela va parcourir l'itérateur entier, le sauvegarder dans un tableau (temporaire), l'itérer à nouveau et puis faire la cartographie. Si la liste est énorme (ou même potentiellement infinie), cela entraînera une utilisation inutile de la mémoire.

Bien sûr, si la liste d'éléments est relativement petite, l'utilisation de l'option Array.from devrait être plus que suffisant.

0 votes

Comment une quantité finie de mémoire peut-elle contenir une structure de données infinie ?

5 votes

Ça ne l'est pas, c'est le but. En utilisant ceci, vous pouvez créer des "flux de données" en enchaînant une source d'itérateurs à un ensemble de transformations d'itérateurs et enfin à un puits de consommateurs. Par exemple, pour le traitement audio en continu, le travail avec des fichiers volumineux, les agrégateurs de bases de données, etc.

1 votes

J'aime cette réponse. Quelqu'un peut-il recommander une bibliothèque qui offre des méthodes de type Array sur les itérables ?

16voto

Ian Storm Taylor Points 1883

La méthode la plus simple et la plus performante consiste à utiliser le deuxième argument de la commande Array.from pour 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 non-infini itérable. Et cela évite d'avoir à utiliser un appel séparé à Array.from(map).map(...) qui itérerait deux fois dans l'itérable et serait moins performant.

3voto

MartyO256 Points 16

Vous pourriez récupérer un itérateur sur l'itérable, puis renvoyer un autre itérateur qui appelle la fonction de rappel du 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

0 votes

Qu'est-ce que ça donnerait en typographie ?

1voto

dimadeveatii Points 21

Vous pourriez 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();

0 votes

Joli ! C'est comme ça que les API de JS auraient dû être faites. Comme toujours, Rust a raison : doc.rust-lang.org/std/iter/trait.Iterator.html

0 votes

"Comme toujours, Rust a raison", bien sûr... Il y a une proposition de standardisation pour toutes sortes de fonctions d'aide pour l'interface des itérateurs. github.com/tc39/proposal-iterator-helpers Vous pouvez l'utiliser dès aujourd'hui avec corejs en important l'option from fn de "core-js-pure/features/iterator" qui renvoie le "nouvel" itérateur.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X