Iterando sobre el mapa mecanografiado

134

Estoy tratando de iterar sobre un mapa mecanografiado, pero sigo recibiendo errores y todavía no puedo encontrar ninguna solución para un problema tan trivial.

Mi código es:

myMap : Map<string, boolean>;
for(let key of myMap.keys()) {
   console.log(key);
}

Y me sale el error:

El tipo 'IterableIteratorShim <[string, boolean]>' no es un tipo de matriz o un tipo de cadena.

Seguimiento completo de la pila:

 Error: Typescript found the following errors:
  /home/project/tmp/broccoli_type_script_compiler-input_base_path-q4GtzHgb.tmp/0/src/app/project/project-data.service.ts (21, 20): Type 'IterableIteratorShim<[string, boolean]>' is not an array type or a string type.
    at BroccoliTypeScriptCompiler._doIncrementalBuild (/home/project/node_modules/angular-cli/lib/broccoli/broccoli-typescript.js:115:19)
    at BroccoliTypeScriptCompiler.build (/home/project/node_modules/angular-cli/lib/broccoli/broccoli-typescript.js:43:10)
    at /home/project/node_modules/broccoli-caching-writer/index.js:152:21
    at lib$rsvp$$internal$$tryCatch (/home/project/node_modules/rsvp/dist/rsvp.js:1036:16)
    at lib$rsvp$$internal$$invokeCallback (/home/project/node_modules/rsvp/dist/rsvp.js:1048:17)
    at lib$rsvp$$internal$$publish (/home/project/node_modules/rsvp/dist/rsvp.js:1019:11)
    at lib$rsvp$asap$$flush (/home/project/node_modules/rsvp/dist/rsvp.js:1198:9)
    at _combinedTickCallback (internal/process/next_tick.js:67:7)
    at process._tickCallback (internal/process/next_tick.js:98:9)

Estoy usando angular-cli beta5 y typecript 1.8.10 y mi objetivo es es5. ¿Alguien ha tenido este problema?

mwe
fuente
44
Ver esta respuesta de github github.com/Microsoft/TypeScript/issues/…
yurzui

Respuestas:

210

Podrías usar Map.prototype.forEach((value, key, map) => void, thisArg?) : voiden su lugar

Úselo así:

myMap.forEach((value: boolean, key: string) => {
    console.log(key, value);
});
rinukkusu
fuente
15
Acabo de encontrarme con esto. No parece que TypeScript esté respetando las especificaciones para la iteración del mapa, al menos de acuerdo con MDN, que especifica un bucle for-of. .forEaches subóptimo, ya que no puedes romperlo, AFAIK
Samjones
14
No sé por qué su valor, entonces clave. Parece al revés.
Paul Rooney
@PaulRooney es así probablemente para armonizar Array.prototype.map.
Ahmed Fasih
1
¿Cómo detener este proceso de iteración si encontramos lo que necesitamos?
JavaRunner
36

es6

for (let [key, value] of map) {
    console.log(key, value);
}

es5

for (let entry of Array.from(map.entries())) {
    let key = entry[0];
    let value = entry[1];
}
Oded Breiner
fuente
1
La versión es6 dada anteriormente no se está compilando en modo estricto.
Stephane
Gracias buen señor.
Kitanga Nday
36

Solo usa el Array.from()método para convertirlo a Array:

myMap : Map<string, boolean>;
for(let key of Array.from( myMap.keys()) ) {
   console.log(key);
}
6qat
fuente
55
La conversión de mapas es una operación que requiere mucho rendimiento y no es la forma correcta de resolver un problema que es esencialmente el compilador que oculta las partes centrales del lenguaje. Simplemente <any>emite el mapa para iterarlo con for-of.
Kilves
1
Cuidado: pensé que la sugerencia de @Kilves anterior para enviar el mapa a "any" era una solución elegante. Cuando lo hice, el código se compiló y ejecutó sin quejarse, pero el Mapa no se repitió en realidad: el contenido del bucle nunca se ejecutó. La Array.from()estrategia propuesta aquí funcionó para mí.
mactyr
Intenté eso también, tampoco funcionó para mí, lo que es incluso más estúpido teniendo en cuenta que es parte de ES6 y debería "funcionar" en la mayoría de los navegadores. Pero supongo que nuestros señores angulares usan un poco de magia jumbo en zone.js para que no funcione, porque odian a ES6. Suspiro.
Kilves
28

Usando Array.from , Array.prototype.forEach () y funciones de flecha :

Iterar sobre las teclas :

Array.from(myMap.keys()).forEach(key => console.log(key));

Iterar sobre los valores :

Array.from(myMap.values()).forEach(value => console.log(value));

Iterar sobre las entradas :

Array.from(myMap.entries()).forEach(entry => console.log('Key: ' + entry[0] + ' Value: ' + entry[1]));
Tobold
fuente
2
No estoy seguro de por qué, tengo el mapa como Map <String, CustomeClass>. ninguno de los métodos anteriores funcionó, excepto Array.from (myMap.values ​​()). forEach (value => console.log (value)) ;.
Rajashree Gr
27

Esto funcionó para mí. Versión de TypeScript: 2.8.3

for (const [key, value] of Object.entries(myMap)) { 
    console.log(key, value);
}
Debashish Sen
fuente
77
Obtuve esto para trabajar cambiando Object.entries (myMap) a solo myMap.entries (). Me gusta esta respuesta porque evita los errores de manejo de errores de las llamadas .forEach.
encierro
2
A tener en cuenta que si tu targeten tsconfiges es5esto arroja un error, pero con es6las obras correctamente. También puede hacerlo for (const [key, value] of myMap)cuando apuntaes6
Benjamin Vogler,
16

Según las notas de la versión de TypeScript 2.3 en "Nuevo --downlevelIteration" :

for..of statements, Destrucción de matrices y elementos de propagación en matrices, llamadas y nuevas expresiones admiten Symbol.iterator en ES5 / E3 si está disponible cuando se usa --downlevelIteration

¡Esto no está habilitado por defecto! Agregue "downlevelIteration": truea su tsconfig.json, o pase la --downlevelIterationbandera a tsc, para obtener soporte completo para el iterador.

Con esto en su lugar, puede escribir for (let keyval of myMap) {...}y keyvalel tipo se deducirá automáticamente.


¿Por qué está desactivado por defecto? Según el colaborador de TypeScript @aluanhaddad ,

Es opcional porque tiene un impacto muy significativo en el tamaño del código generado, y potencialmente en el rendimiento, para todos los usos de iterables (incluidas las matrices).

Si puede apuntar a ES2015 ( "target": "es2015"en tsconfig.jsono tsc --target ES2015) o posterior, la habilitación downlevelIterationes obvia, pero si está apuntando a ES5 / ES3, puede hacer una referencia para asegurarse de que el soporte del iterador no afecte el rendimiento (si lo hace, podría ser mejor apagado con Array.fromconversión o forEachalguna otra solución).

Ahmed Fasih
fuente
¿Está bien o es peligroso habilitar esto cuando se usa Angular?
Simon_Weaver
9

Esto funcionó para mí.

Object.keys(myMap).map( key => {
    console.log("key: " + key);
    console.log("value: " + myMap[key]);
});
Jason Slobotski
fuente
2
Sin embargo
Ixx
8

Estoy usando el último TS y nodo (v2.6 y v8.9 respectivamente) y puedo hacer:

let myMap = new Map<string, boolean>();
myMap.set("a", true);
for (let [k, v] of myMap) {
    console.log(k + "=" + v);
}
lazieburd
fuente
¿Puedes confirmar que primero tuviste que configurar "downlevelIteration": truetu tsconfig.json?
Ahmed Fasih
3
No lo he downlevelIteratonestablecido, mi objetivo es es2017sin embargo.
lazieburd
¿No puedo hacer esto para el elemento Map <string, CustomClass []>? el compilador dice que no es una matriz de tipo o tipo de cadena.
Rajashree Gr
esto no funciona para mí en 2,8 - consigoType 'Map<K, V>' is not an array type or a string type.
Simon_Weaver
3

También puede aplicar el método de mapa de matriz a Map.entries () iterable:

[...myMap.entries()].map(
     ([key, value]: [string, number]) => console.log(key, value)
);

Además, como se señaló en otras respuestas, es posible que deba habilitar la iteración de nivel inferior en su tsconfig.json (en las opciones del compilador):

  "downlevelIteration": true,
cham
fuente
Esta característica se introdujo en TypeScript 2.3. El problema ocurrió con TypeScript 1.8.10
mwe
Si no puede usar "downlevelIteration", puede usar:const projected = Array.from(myMap).map(...);
Efrain
1

Solo una explicación simple para usarlo en un documento HTML.

Si tiene un Mapa de tipos (clave, matriz), entonces inicialice la matriz de esta manera:

public cityShop: Map<string, Shop[]> = new Map();

Y para iterar sobre él, crea una matriz a partir de valores clave.

Solo úsalo como una matriz como en:

keys = Array.from(this.cityShop.keys());

Luego, en HTML, puede usar:

*ngFor="let key of keys"

Dentro de este ciclo, solo obtienes el valor de la matriz con:

this.cityShop.get(key)

¡Hecho!

Sophie C Musical
fuente
1

En Typecript 3.5 y Angular 8 LTS, se requería emitir el tipo de la siguiente manera:

for (let [k, v] of Object.entries(someMap)) {
    console.log(k, v)
}
Constantin Konstantinidis
fuente
-1

Si realmente no le gustan las funciones anidadas, también puede iterar sobre las teclas:

myMap : Map<string, boolean>;
for(let key of myMap) {
   if (myMap.hasOwnProperty(key)) {
       console.log(JSON.stringify({key: key, value: myMap[key]}));
   }
}

Tenga en cuenta que debe filtrar las iteraciones no clave con hasOwnProperty, si no lo hace, recibirá una advertencia o un error.

peterh - Restablece a Monica
fuente
¿Cómo iterar sobre el mapa en HTML? no parece funcionar en absoluto. <div ng-repeat = "(clave, valor) en model.myMap"> {{clave}}. </div>
Shinya Koizumi el
@ powerfade917 No funciona, solo funciona para matrices porque angular es un montón de basura. Pero haga esto como una nueva pregunta y así aprenderá que angular no es un montón de basura, sino que debe convertirlo en una matriz. Tenga en cuenta que tampoco es la parte superior de la parte superior de la programación, porque aparentemente es incapaz de diferenciar entre angular y mecanografiado.
peterh - Restablece a Mónica el