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ż --downlevelIterationją tsc, 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.