Digamos que temos um Mapa :, let m = new Map();
usando m.values()
retorna um iterador de mapa.
Mas não posso usar forEach()
ou map()
naquele iterador e implementar um loop while nesse iterador parece um anti-padrão, já que o ES6 oferece funções como map()
.
Então, há uma maneira de usar map()
em um iterador?
javascript
dictionary
syntax
ecmascript-6
iterator
Shinzou
fonte
fonte
lodash
map
funções que também oferecem suporte ao mapa.Array.from(m.values()).map(...)
funciona, mas acho que não é a melhor maneira de fazer isso.Array#map
?Respostas:
A maneira mais simples e de menor desempenho de fazer isso é:
Array.from(m).map(([key,value]) => /* whatever */)
Melhor ainda
Array.from(m, ([key, value]) => /* whatever */))
Array.from
pega qualquer coisa iterável ou semelhante a um array e o converte em um array! Como Daniel aponta nos comentários, podemos adicionar uma função de mapeamento à conversão para remover uma iteração e, subsequentemente, um array intermediário.Usar
Array.from
irá mover seu desempenho deO(1)
paraO(n)
como @hraban aponta nos comentários. Comom
é umMap
e eles não podem ser infinitos, não precisamos nos preocupar com uma sequência infinita. Na maioria dos casos, isso será suficiente.Existem algumas outras maneiras de percorrer um mapa.
Usando
forEach
m.forEach((value,key) => /* stuff */ )
Usando
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
fonte
Array.from(m, ([key,value]) => /* whatever */)
(observe que a função de mapeamento está dentro dofrom
) e, em seguida, nenhum array intermediário é criado ( fonte ). Ele ainda se move de O (1) para O (n), mas pelo menos a iteração e o mapeamento acontecem em apenas uma iteração completa.Você poderia definir outra função iteradora para fazer um loop sobre isso:
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(','));
Agora você pode perguntar: por que não usar
Array.from
? Uma vez que esta será executada através de toda a iteração, guardá-lo para uma matriz (temporária), iterar-lo novamente e , em seguida, fazer o mapeamento. Se a lista for enorme (ou mesmo potencialmente infinita), isso levará ao uso desnecessário da memória.Claro, se a lista de itens for bastante pequena, o uso
Array.from
deve ser mais do que suficiente.fonte
mapIterator()
não garante que o iterador subjacente será devidamente fechado (iterator.return()
chamado), a menos que o próximo valor de retorno tenha sido chamado pelo menos uma vez. Veja: repeater.js.org/docs/safetyA maneira mais simples e eficiente é usar o segundo argumento
Array.from
para conseguir isso:const map = new Map() map.set('a', 1) map.set('b', 2) Array.from(map, ([key, value]) => `${key}:${value}`) // ['a:1', 'b:2']
Essa abordagem funciona para qualquer iterável não infinito . E evita ter que usar uma chamada separada para a
Array.from(map).map(...)
qual iteraria por meio do iterável duas vezes e seria pior para o desempenho.fonte
Você poderia recuperar um iterador sobre o iterável e, em seguida, retornar outro iterador que chama a função de retorno de chamada de mapeamento em cada elemento iterado.
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
fonte
Você pode usar itiriri que implementa métodos semelhantes a array para iteráveis:
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();
fonte
Dê uma olhada em https://www.npmjs.com/package/fluent-iterable
Funciona com todos os iteráveis (Mapa, função do gerador, array) e iteráveis assíncronos.
const map = new Map(); ... console.log(fluent(map).filter(..).map(..));
fonte