Iterieren über Typescript Map

134

Ich versuche, eine Typoskript-Map zu durchlaufen, erhalte jedoch immer wieder Fehler und konnte noch keine Lösung für ein so triviales Problem finden.

Mein Code lautet:

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

Und ich bekomme den Fehler:

Der Typ 'IterableIteratorShim <[string, boolean]>' ist kein Array-Typ oder String-Typ.

Full Stack Trace:

 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)

Ich verwende Angular-Cli Beta5 und Typoskript 1.8.10 und mein Ziel ist es5. Hat jemand dieses Problem gehabt?

mwe
quelle
4
Siehe diese Antwort von github github.com/Microsoft/TypeScript/issues/…
yurzui

Antworten:

210

Sie könnten Map.prototype.forEach((value, key, map) => void, thisArg?) : voidstattdessen verwenden

Verwenden Sie es so:

myMap.forEach((value: boolean, key: string) => {
    console.log(key, value);
});
Rinukkusu
quelle
15
Bin gerade darauf gestoßen. Es sieht nicht so aus, als würde TypeScript die Spezifikation für die Karteniteration respektieren, zumindest gemäß MDN, das eine for-of-Schleife angibt. .forEachist nicht optimal, da man es nicht brechen kann, AFAIK
Samjones
14
Keine Ahnung, warum sein Wert dann der Schlüssel ist. Scheint rückwärts.
Paul Rooney
@PaulRooney es ist so, wahrscheinlich mit zu harmonieren Array.prototype.map.
Ahmed Fasih
1
Wie kann ich diesen Iterationsprozess stoppen, wenn wir gefunden haben, was wir brauchen?
JavaRunner
36

Verwenden Sie einfach die Array.from()Methode, um es in eine zu konvertierenArray :

myMap : Map<string, boolean>;
for(let key of Array.from( myMap.keys()) ) {
   console.log(key);
}
6qat
quelle
5
Das Konvertieren von Maps ist ein sehr leistungshungriger Vorgang und nicht der richtige Weg, um ein Problem zu lösen, bei dem im Wesentlichen nur der Compiler die Kernteile der Sprache versteckt. Nur <any>-Cast der Karte mit für-of iterieren.
Kilves
1
Achtung: Ich dachte, dass der Vorschlag von @Kilves oben, die Karte auf "any" zu setzen, eine elegante Problemumgehung ist. Als ich es tat, wurde der Code kompiliert und ohne Beanstandung ausgeführt, aber die Map wurde nicht tatsächlich iteriert - der Inhalt der Schleife wurde nie ausgeführt. Die Array.from()hier vorgeschlagene Strategie hat bei mir funktioniert.
Mactyr
Ich versuchte auch , dass es für mich auch nicht funktioniert hat, die sogar dümmer erwägt es ist Teil des ES6 und sollte „nur Arbeit“ auf den meisten Browsern. Aber ich denke, unsere eckigen Overlords verwenden einen magischen Hokuspokus in zone.js, damit es nicht funktioniert, weil sie ES6 hassen. Seufzer.
Kilves
35

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
quelle
1
Die oben angegebene es6-Version wird nicht im strengen Modus kompiliert.
Stephane
Ich danke Ihnen lieber Herr.
Kitanga Nday
28

Mit Array.from , Array.prototype.forEach () und Pfeil - Funktionen :

Über die Tasten iterieren :

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

Iterieren Sie über die Werte :

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

Durchlaufen Sie die Einträge :

Array.from(myMap.entries()).forEach(entry => console.log('Key: ' + entry[0] + ' Value: ' + entry[1]));
Tobold
quelle
2
Ich weiß nicht warum, ich habe die Karte wie Map <String, CustomeClass>. Keine der oben genannten Methoden funktionierte außer Array.from (myMap.values ​​()). forEach (value => console.log (value));.
Rajashree Gr
27

Das hat bei mir funktioniert. TypeScript-Version: 2.8.3

for (const [key, value] of Object.entries(myMap)) { 
    console.log(key, value);
}
Debashish Sen.
quelle
7
Ich habe dies zum Laufen gebracht, indem ich Object.entries (myMap) in myMap.entries () geändert habe. Ich mag diese Antwort, weil sie die Fehler bei der Behandlung von .forEach-Aufrufen vermeidet.
Encrest
2
Es ist erwähnenswert, dass wenn Ihr targetIn tsconfigist, es5dies einen Fehler auslöst, aber mit es6funktioniert richtig. Sie können auch nur for (const [key, value] of myMap)beim Zielen tunes6
Benjamin Vogler
16

Gemäß den Versionshinweisen zu TypeScript 2.3 zu "Neu --downlevelIteration" :

for..of statementsDie Elemente "Array Destructuring" und "Spread" in den Ausdrücken "Array", "Call" und "New" unterstützen Symbol.iterator in ES5 / E3, sofern bei Verwendung verfügbar --downlevelIteration

Dies ist standardmäßig nicht aktiviert! Fügen Sie "downlevelIteration": trueIhr Flag hinzu tsconfig.jsonoder übergeben Sie es --downlevelIterationan tsc, um vollständige Iterator-Unterstützung zu erhalten.

Wenn dies vorhanden ist, können Sie schreiben for (let keyval of myMap) {...}und keyvalder Typ wird automatisch abgeleitet.


Warum ist dies standardmäßig deaktiviert? Nach Typoskript Beitrag @aluanhaddad ,

Es ist optional, da es einen erheblichen Einfluss auf die Größe des generierten Codes und möglicherweise auf die Leistung für alle Verwendungen von Iterables (einschließlich Arrays) hat.

Wenn Sie auf ES2015 ( "target": "es2015"in tsconfig.jsonoder tsc --target ES2015) oder höher downlevelIterationabzielen können, ist das Aktivieren ein Kinderspiel. Wenn Sie jedoch auf ES5 / ES3 abzielen, können Sie einen Benchmark durchführen, um sicherzustellen, dass die Iteratorunterstützung die Leistung nicht beeinträchtigt (wenn dies der Fall ist, sind Sie möglicherweise besser aus mit Array.fromKonvertierung forEachoder einer anderen Problemumgehung).

Ahmed Fasih
quelle
Ist dies in Ordnung oder gefährlich, um dies bei Verwendung von Angular zu aktivieren?
Simon_Weaver
9

Das hat bei mir funktioniert.

Object.keys(myMap).map( key => {
    console.log("key: " + key);
    console.log("value: " + myMap[key]);
});
Jason Slobotski
quelle
2
Damit sind die Schlüssel aber immer Strings
Ixx
8

Ich verwende den neuesten TS und Knoten (v2.6 bzw. v8.9) und kann Folgendes tun:

let myMap = new Map<string, boolean>();
myMap.set("a", true);
for (let [k, v] of myMap) {
    console.log(k + "=" + v);
}
lazieburd
quelle
Können Sie bestätigen, dass Sie zuerst "downlevelIteration": trueIhre einstellen mussten tsconfig.json?
Ahmed Fasih
3
Ich habe nicht downlevelIteratongesetzt, mein Ziel ist es2017jedoch.
Lazieburd
Kann ich das nicht für das Map <string, CustomClass []> Element tun? Der Compiler gibt an, dass es sich nicht um ein Array vom Typ oder String-Typ handelt.
Rajashree Gr
das funktioniert nicht für mich auf 2.8 - ich Type 'Map<K, V>' is not an array type or a string type.
verstehe
3

Sie können die Array-Map-Methode auch auf die iterable Map.entries () anwenden:

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

Wie in anderen Antworten erwähnt, müssen Sie möglicherweise die Iteration auf niedriger Ebene in Ihrer Datei tsconfig.json aktivieren (unter Compileroptionen):

  "downlevelIteration": true,
cham
quelle
Diese Funktion wurde in TypeScript 2.3 eingeführt. Das Problem trat mit TypeScript 1.8.10
mwe
Wenn Sie "downlevelIteration" nicht verwenden können, können Sie Folgendes verwenden:const projected = Array.from(myMap).map(...);
Efrain
1

Nur eine einfache Erklärung, um es in einem HTML-Dokument zu verwenden.

Wenn Sie eine Karte mit Typen (Schlüssel, Array) haben, initialisieren Sie das Array folgendermaßen:

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

Und um darüber zu iterieren, erstellen Sie ein Array aus Schlüsselwerten.

Verwenden Sie es einfach als Array wie in:

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

Dann können Sie in HTML Folgendes verwenden:

*ngFor="let key of keys"

Innerhalb dieser Schleife erhalten Sie nur den Array-Wert mit:

this.cityShop.get(key)

Getan!

Sophie C Musical
quelle
1

In Typescript 3.5 und Angular 8 LTS musste der Typ wie folgt umgewandelt werden:

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

Wenn Sie verschachtelte Funktionen nicht wirklich mögen, können Sie auch über die Tasten iterieren:

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

Beachten Sie, dass Sie die Nicht-Schlüssel-Iterationen mit herausfiltern hasOwnPropertymüssen. Wenn Sie dies nicht tun, erhalten Sie eine Warnung oder einen Fehler.

Peter - Setzen Sie Monica wieder ein
quelle
Wie iteriere ich über eine Karte in HTML? es scheint überhaupt nicht zu funktionieren. <div ng-repeat = "(Schlüssel, Wert) in model.myMap"> {{key}}. </ div>
Shinya Koizumi
@ powerfade917 Es funktioniert nicht, es funktioniert nur für Arrays, da eckig ein Müllhaufen ist. Wenn Sie dies jedoch als neue Frage stellen, werden Sie feststellen, dass Angular kein Müllhaufen ist, sondern dass Sie ihn in ein Array konvertieren müssen. Beachten Sie auch, dass Sie nicht ganz oben in der Programmierung stehen, da Sie scheinbar nicht in der Lage sind, zwischen Winkel- und Typoskript zu unterscheiden.
Peterh