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?


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

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

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

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

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

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


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

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

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,

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!


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

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


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
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.