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?


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);
});

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];
}

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);
}

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]));

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);
}

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).


¿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]);
});

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);
}

¿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,

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!


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)
}

-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.


¿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
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.