Iteracja po mapie Typescript


136

Próbuję iterować po mapie maszynopisu, ale ciągle pojawiają się błędy i nie mogłem jeszcze znaleźć rozwiązania dla tak trywialnego problemu.

Mój kod to:

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

I otrzymuję błąd:

Typ „IterableIteratorShim <[string, boolean]>” nie jest tablicą ani ciągiem.

Pełny ślad stosu:

 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)

Używam angular-cli beta5 i maszynopisu 1.8.10, a moim celem jest es5. Czy ktoś miał ten problem?


4
Zobacz tę odpowiedź z github github.com/Microsoft/TypeScript/issues/…
yurzui

Odpowiedzi:


214

Możesz Map.prototype.forEach((value, key, map) => void, thisArg?) : voidzamiast tego użyć

Użyj tego w ten sposób:

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

16
Właśnie wpadłem na to. Nie wygląda na to, że TypeScript przestrzega specyfikacji iteracji mapy, przynajmniej zgodnie z MDN, który określa pętlę for-of. .forEachjest nieoptymalny, ponieważ nie możesz go złamać, AFAIK
Samjones

14
nie wiem, dlaczego jego wartość to klucz. Wydaje się odwrócony.
Paul Rooney,

@PaulRooney to chyba tak, aby zharmonizować Array.prototype.map.
Ahmed Fasih

1
Jak zatrzymać ten proces iteracji, jeśli znaleźliśmy to, czego potrzebujemy?
JavaRunner

37

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
Podana powyżej wersja es6 nie jest kompilowana w trybie ścisłym.
Stephane

Uprzejmie panu dziękuję.
Kitanga Nday

36

Po prostu użyj Array.from()metody, aby przekonwertować go na Array:

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

5
Konwertowanie map jest operacją wymagającą dużej wydajności i nie jest właściwym sposobem rozwiązania problemu, który zasadniczo polega tylko na tym, że kompilator ukrywa podstawowe części języka. Po prostu <any>wyślij mapę, aby wykonać iterację za pomocą for-of.
Kilves,

1
Uwaga: uważam, że sugestia @Kilves powyżej, aby przesłać mapę na „dowolny”, była eleganckim obejściem. Kiedy to zrobiłem, kod skompilował się i działał bez reklamacji, ale Mapa nie była w rzeczywistości iterowana - zawartość pętli nigdy nie została wykonana. Array.from()Strategia proponowana tu nie działa dla mnie.
mactyr

Ja też tego próbowałem, ale to też mi się nie udało, co jest jeszcze głupsze, biorąc pod uwagę, że jest częścią ES6 i powinno „po prostu działać” na większości przeglądarek. Ale wydaje mi się, że nasi kanciści władcy używają magicznych mumbo jumbo w zone.js, aby to nie zadziałało, ponieważ nienawidzą ES6. Westchnienie.
Kilves,

28

Korzystanie z funkcji Array.from , Array.prototype.forEach () i strzałkowych :

Powtarzaj po klawiszach :

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

Iteruj po wartościach :

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

Powtarzaj wpisy :

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

2
Nie wiem dlaczego, mam mapę taką jak Map <String, CustomeClass>. żadna z powyższych metod nie działała poza Array.from (myMap.values ​​()). forEach (value => console.log (value)) ;.
Rajashree Gr

27

To zadziałało dla mnie. Wersja TypeScript: 2.8.3

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

7
Mam to do pracy, zmieniając Object.entries (myMap) na po prostu myMap.entries (). Podoba mi się ta odpowiedź, ponieważ pozwala uniknąć pułapek obsługi błędów wywołań .forEach.
spotkanie

2
Warto zauważyć, że jeśli targetw tsconfigjest es5to zgłasza błąd, ale es6działa poprawnie. Możesz też po prostu zrobić for (const [key, value] of myMap)celowaniees6
Benjamin Vogler

16

Zgodnie z uwagami do wydania TypeScript 2.3 „Nowy --downlevelIteration :

for..of statements, Niszczenie tablic i rozkładanie elementów w wyrażeniach Array, Call i New obsługują Symbol.iterator w ES5 / E3, jeśli są dostępne podczas używania --downlevelIteration

Nie jest to domyślnie włączone! Dodaj "downlevelIteration": truedo swojej flagi tsconfig.jsonlub przekaż --downlevelIterationtsc, aby uzyskać pełną obsługę iteratora.

Z tego miejsca, możesz pisać for (let keyval of myMap) {...}i keyval„s typ zostanie automatycznie wnioskować.


Dlaczego jest to domyślnie wyłączone? Według autora TypeScript @aluanhaddad ,

Jest opcjonalny, ponieważ ma bardzo duży wpływ na rozmiar generowanego kodu i potencjalnie na wydajność dla wszystkich zastosowań iterowalnych (w tym tablic).

Jeśli możesz kierować reklamy na ES2015 ( "target": "es2015"w wersji tsconfig.jsonlub tsc --target ES2015) lub później, włączenie downlevelIterationjest oczywiste, ale jeśli celujesz w ES5 / ES3, możesz wykonać test porównawczy, aby upewnić się, że obsługa iteratorów nie wpływa na wydajność (jeśli tak, możesz być lepszy wyłączyć z Array.fromkonwersją lub w forEachinny sposób).


czy włączenie tej opcji podczas korzystania z Angular jest w porządku czy niebezpieczne?
Simon_Weaver

9

To zadziałało dla mnie.

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

2
Dzięki temu klucze zawsze będą jednak ciągami
Ixx

8

Używam najnowszego TS i węzła (odpowiednio v2.6 i v8.9) i mogę:

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

Czy możesz potwierdzić, że najpierw musiałeś ustawić "downlevelIteration": truew swoim tsconfig.json?
Ahmed Fasih

3
Nie mam downlevelIteratonwyznaczonego, mój cel jest es2017jednak.
lazieburd

Nie mogę tego zrobić dla elementu Map <string, CustomClass []>? kompilator mówi, że nie jest to tablica typu ani typu string.
Rajashree Gr

to nie działa dla mnie na 2.8 - dostajęType 'Map<K, V>' is not an array type or a string type.
Simon_Weaver

3

Możesz również zastosować metodę mapowania tablic do iterowalnej metody Map.entries ():

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

Ponadto, jak zauważono w innych odpowiedziach, może być konieczne włączenie iteracji niższego poziomu w tsconfig.json (w opcjach kompilatora):

  "downlevelIteration": true,

Ta funkcja została wprowadzona w języku TypeScript 2.3. Problem wystąpił z TypeScript 1.8.10
mwe

Jeśli nie możesz użyć "downlevelIteration", możesz użyć:const projected = Array.from(myMap).map(...);
Efrain

1

Wystarczy proste wyjaśnienie, jak używać go w dokumencie HTML.

Jeśli masz Mapę typów (klucz, tablica), to inicjalizujesz tablicę w ten sposób:

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

Aby wykonać iterację, tworzysz tablicę z wartości kluczowych.

Po prostu użyj go jako tablicy, jak w:

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

Następnie w HTML możesz użyć:

*ngFor="let key of keys"

Wewnątrz tej pętli otrzymujesz wartość tablicy za pomocą:

this.cityShop.get(key)

Gotowe!


1

W przypadku Typescript 3.5 i Angular 8 LTS wymagane było rzutowanie typu w następujący sposób:

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

-1

Jeśli naprawdę nie lubisz funkcji zagnieżdżonych, możesz także iterować po klawiszach:

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

Uwaga, musisz odfiltrować iteracje niebędące kluczami za pomocą hasOwnProperty, jeśli tego nie zrobisz, otrzymasz ostrzeżenie lub błąd.


jak iterować mapę w html? wydaje się, że w ogóle nie działa. <div ng-repeat = "(klucz, wartość) w model.myMap"> {{klucz}}. </div>
Shinya Koizumi

@ powerfade917 To nie działa, działa tylko dla tablic, ponieważ angular to kupa śmieci. Ale zadaj to jako nowe pytanie, a dowiesz się, że angular nie jest kupą śmieci, ale musisz go przekonwertować na tablicę. Zwróć uwagę, że nie jesteś na szczycie programowania, ponieważ pozornie nie jesteś w stanie odróżnić pisma kątowego od maszynopisu.
peterh - Przywróć Monikę
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.