Iterazione sulla mappa dattiloscritta


135

Sto cercando di iterare su una mappa dattiloscritta ma continuo a ricevere errori e non sono ancora riuscito a trovare alcuna soluzione per un problema così banale.

Il mio codice è:

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

E ottengo l'errore:

Il tipo "IterableIteratorShim <[string, boolean]>" non è un tipo di matrice o un tipo di stringa.

Traccia stack completo:

 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)

Sto usando angular-cli beta5 e dattiloscritto 1.8.10 e il mio obiettivo è es5. Qualcuno ha avuto questo problema?


4
Vedi questa risposta da github github.com/Microsoft/TypeScript/issues/…
yurzui

Risposte:


212

Potresti usare Map.prototype.forEach((value, key, map) => void, thisArg?) : voidinvece

Usalo in questo modo:

myMap.forEach((value: boolean, key: string) => {
    console.log(key, value);
});

15
Mi sono imbattuto in questo. Non sembra che TypeScript stia rispettando le specifiche per l'iterazione della mappa, almeno secondo MDN che specifica un ciclo for-of. .forEachnon è ottimale, dal momento che non puoi romperlo, AFAIK
Samjones

14
non so perché il suo valore quindi chiave. Sembra al contrario.
Paul Rooney

@PaulRooney è così probabilmente per armonizzarsi con Array.prototype.map.
Ahmed Fasih

1
Come fermare questo processo di iterazione se abbiamo trovato ciò di cui abbiamo bisogno?
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 versione es6 di cui sopra non si compila in modalità rigorosa.
Stephane

Grazie gentile signore.
Kitanga Nday

36

Usa semplicemente il Array.from()metodo per convertirlo in un Array:

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

5
La conversione delle mappe è un'operazione molto impegnativa in termini di prestazioni e non il modo corretto per risolvere un problema che è essenzialmente solo il compilatore che nasconde le parti fondamentali del linguaggio. <any>Trasmetti semplicemente la mappa per iterarla con for-of.
Kilves

1
Attenzione: ho pensato che il suggerimento di @Kilves sopra di lanciare la mappa su "qualsiasi" fosse una soluzione elegante. Quando l'ho fatto, il codice è stato compilato ed è stato eseguito senza lamentele, ma la mappa non è stata effettivamente iterata: il contenuto del ciclo non è mai stato eseguito. La Array.from()strategia qui proposta ha funzionato per me.
mactyr

Ho provato anche quello, non ha funzionato nemmeno per me, il che è ancora più stupido considerando che fa parte di ES6 e dovrebbe "funzionare" sulla maggior parte dei browser. Ma immagino che i nostri signori spigolosi usino alcuni magici mumbo jumbo in zone.js per non farlo funzionare, perché odiano ES6. Sospiro.
Kilves

28

Utilizzando Array.from , Array.prototype.forEach () e le funzioni freccia :

Itera sulle chiavi :

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

Iterare sui valori :

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

Scorri le voci :

Array.from(myMap.entries()).forEach(entry => console.log('Key: ' + entry[0] + ' Value: ' + entry[1]));

2
Non so perché, ho la mappa come Map <String, CustomeClass>. nessuno dei metodi precedenti ha funzionato tranne Array.from (myMap.values ​​()). forEach (value => console.log (value)) ;.
Rajashree Gr

27

Questo ha funzionato per me. Versione TypeScript: 2.8.3

for (const [key, value] of Object.entries(myMap)) { 
    console.log(key, value);
}

7
Ho ottenuto questo per funzionare modificando Object.entries (myMap) solo myMap.entries (). Mi piace questa risposta perché evita le insidie ​​di gestione degli errori delle chiamate .forEach.
encrest

2
Vale la pena notare che se il tuo targetin tsconfigè es5questo genera un errore, ma con es6funziona correttamente. Puoi anche farlo for (const [key, value] of myMap)quando prendi di miraes6
Benjamin Vogler

16

Secondo le note di rilascio di TypeScript 2.3 su "Nuovo --downlevelIteration" :

for..of statements, Gli elementi Array Destructuring e Spread nelle espressioni Array, Call e New supportano Symbol.iterator in ES5 / E3 se disponibile quando si utilizza --downlevelIteration

Questo non è abilitato per impostazione predefinita! Aggiungi "downlevelIteration": trueal tuo tsconfig.json, o passa --downlevelIterationflag a tsc, per ottenere il supporto completo dell'iteratore.

Con questa impostazione, puoi scrivere for (let keyval of myMap) {...}e keyvalil tipo di verrà dedotto automaticamente.


Perché è disattivato per impostazione predefinita? Secondo il collaboratore di TypeScript @aluanhaddad ,

È opzionale perché ha un impatto molto significativo sulla dimensione del codice generato e potenzialmente sulle prestazioni per tutti gli usi degli iterabili (inclusi gli array).

Se puoi scegliere come target ES2015 ( "target": "es2015"in tsconfig.jsono tsc --target ES2015) o successivo, l'abilitazione downlevelIterationè un gioco da ragazzi, ma se stai prendendo di mira ES5 / ES3, potresti eseguire un benchmark per assicurarti che il supporto dell'iteratore non influisca sulle prestazioni (se lo fa, potresti essere migliore con la Array.fromconversione o forEacho qualche altra soluzione alternativa).


è ok o pericoloso abilitarlo quando si utilizza Angular?
Simon_Weaver

9

Questo ha funzionato per me.

Object.keys(myMap).map( key => {
    console.log("key: " + key);
    console.log("value: " + myMap[key]);
});

2
Con questo le chiavi saranno sempre degli archi però
Ixx

8

Sto usando gli ultimi TS e node (rispettivamente v2.6 e v8.9) e posso fare:

let myMap = new Map<string, boolean>();
myMap.set("a", true);
for (let [k, v] of myMap) {
    console.log(k + "=" + v);
}

Puoi confermare che hai dovuto prima impostare "downlevelIteration": truenel tuo tsconfig.json?
Ahmed Fasih

3
Non mi sono downlevelIteratonfissato, il mio obiettivo è es2017comunque.
lazieburd

Non posso farlo per l'elemento Map <string, CustomClass []>? il compilatore dice che non è un array di tipo o tipo stringa.
Rajashree Gr

questo non funziona per me su 2.8 - OttengoType 'Map<K, V>' is not an array type or a string type.
Simon_Weaver

3

Puoi anche applicare il metodo array map all'iterabile Map.entries ():

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

Inoltre, come notato in altre risposte, potresti dover abilitare l'iterazione di livello inferiore nel tuo tsconfig.json (sotto le opzioni del compilatore):

  "downlevelIteration": true,

Questa funzionalità è stata introdotta in TypeScript 2.3. Il problema si è verificato con TypeScript 1.8.10
mwe

Se non puoi utilizzare "downlevelIteration", puoi utilizzare:const projected = Array.from(myMap).map(...);
Efrain

1

Solo una semplice spiegazione per usarlo in un documento HTML.

Se hai una mappa dei tipi (chiave, array), inizializzi l'array in questo modo:

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

E per iterarlo, crei un array dai valori chiave.

Usalo semplicemente come un array come in:

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

Quindi, in HTML, puoi utilizzare:

*ngFor="let key of keys"

All'interno di questo ciclo, ottieni solo il valore dell'array con:

this.cityShop.get(key)

Fatto!


1

Su Typescript 3.5 e Angular 8 LTS, era necessario eseguire il cast del tipo come segue:

for (let [k, v] of Object.entries(someMap)) {
    console.log(k, v)
}

-1

Se non ti piacciono molto le funzioni annidate, puoi anche iterare sui tasti:

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

Nota, devi filtrare le iterazioni non chiave con l'estensione hasOwnProperty , se non lo fai, ottieni un avviso o un errore.


come iterare sulla mappa in html? non sembra funzionare affatto. <div ng-repeat = "(key, value) in model.myMap"> {{key}}. </div>
Shinya Koizumi

@ powerfade917 Non funziona, funziona solo per gli array perché angular è un mucchio di spazzatura. Ma chiedilo come una nuova domanda e così imparerai che angular non è un mucchio di spazzatura, ma devi convertirlo in un array. Nota, inoltre, non sei il vertice della programmazione, perché apparentemente sei incapace di distinguere tra angolare e dattiloscritto.
peterh - Ripristina Monica il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.