Iterando sobre o mapa de texto datilografado


134

Estou tentando iterar em um mapa datilografado, mas continuo recebendo erros e ainda não consegui encontrar nenhuma solução para um problema tão trivial.

Meu código é:

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

E eu recebo o erro:

O tipo 'IterableIteratorShim <[string, boolean]>' não é um tipo de matriz ou de string.

Rastreio de pilha completa:

 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)

Estou usando angular-cli beta5 e datilografado 1.8.10 e meu destino é es5. Alguém já teve esse problema?


4
Veja esta resposta em github github.com/Microsoft/TypeScript/issues/…
yurzui

Respostas:


210

Você poderia usar Map.prototype.forEach((value, key, map) => void, thisArg?) : voidem vez

Use-o assim:

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

15
Apenas corri para isso. Não parece que o TypeScript esteja respeitando a especificação para iteração de mapa, pelo menos de acordo com o MDN, que especifica um loop for-of. .forEaché sub-ideal, já que você não pode quebrá-lo, AFAIK
Samjones

14
não sei por que seu valor então chave. Parece ao contrário.
Paul Rooney

@PaulRooney é assim provavelmente para se harmonizar Array.prototype.map.
Ahmed Fasih 15/07/19

1
Como parar esse processo de iteração se descobrimos o que precisamos?
JavaRunner 3/06

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
A versão es6 fornecida acima não está sendo compilada no modo estrito.
Stephane

Obrigado, gentil senhor.
Kitanga Nday 04/06

36

Basta usar o Array.from()método para convertê-lo em um Array:

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

5
A conversão de mapas é uma operação que demanda muito desempenho e não é a maneira correta de resolver um problema que é basicamente o compilador que oculta as partes principais da linguagem. Apenas <any>-castie o mapa para iterá-lo com for-of.
Kilves

1
Cuidado: achei que a sugestão do @Kilves acima de converter o mapa em "any" era uma solução elegante. Quando o fiz, o código foi compilado e executado sem reclamação, mas o Mapa não foi realmente iterado - o conteúdo do loop nunca foi executado. A Array.from()estratégia proposta aqui funcionou para mim.
mactyr

Eu tentei isso também, também não funcionou para mim, o que é ainda mais estúpido, considerando que faz parte do ES6 e deve "apenas funcionar" na maioria dos navegadores. Mas acho que nossos senhores angulares usam algum jumbo mágico no zone.js para fazer com que não funcione, porque odeiam o ES6. Suspiro.
Kilves

28

Usando as funções Array.from , Array.prototype.forEach () e arrow :

Itere sobre as chaves :

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

Itere sobre os valores :

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

Repita as entradas :

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

2
Não sei por que, eu tenho o mapa como Map <String, CustomeClass>. nenhum dos métodos acima funcionou, exceto Array.from (myMap.values ​​()). forEach (value => console.log (value)) ;.
Rajashree Gr 04/04

27

Isso funcionou para mim. Versão TypeScript: 2.8.3

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

7
Eu consegui isso, alterando Object.entries (myMap) para apenas myMap.entries (). Gosto dessa resposta porque evita as armadilhas de erros de chamadas .forEach.
encrest

2
Vale a pena notar que, se você está targetdentro, isso gera um erro, mas funciona corretamente. Você também pode simplesmente fazer quando o direcionamentotsconfiges5es6for (const [key, value] of myMap)es6
Benjamin Vogler

16

De acordo com as notas de versão--downlevelIteration do TypeScript 2.3 em "Novo " :

for..of statementsOs elementos Array Destructuring e Spread nas expressões Array, Call e New suportam Symbol.iterator no ES5 / E3, se disponível ao usar --downlevelIteration

Isso não está ativado por padrão! Adicione "downlevelIteration": trueao seu tsconfig.json, ou passe o --downlevelIterationsinalizador para tsc, para obter suporte completo ao iterador.

Com isso, você pode escrever for (let keyval of myMap) {...}e keyvalo tipo de letra será automaticamente inferido.


Por que isso está desativado por padrão? De acordo com o colaborador do TypeScript @aluanhaddad ,

É opcional porque tem um impacto muito significativo no tamanho do código gerado e, potencialmente, no desempenho, para todos os usos de iteráveis ​​(incluindo matrizes).

Se você pode segmentar o ES2015 ( "target": "es2015"in tsconfig.jsonou tsc --target ES2015) ou mais recente, a ativação downlevelIterationé fácil, mas se você estiver segmentando o ES5 / ES3, poderá fazer benchmark para garantir que o suporte ao iterador não afete o desempenho (se houver, pode ser melhor com a Array.fromconversão ou forEachoutra solução alternativa).


Isso é bom ou perigoso para habilitar isso ao usar Angular?
Simon_Weaver

9

Isso funcionou para mim.

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

2
Com isso, as chaves sempre serão cordas embora
Ixx

8

Estou usando o TS e o nó mais recentes (v2.6 e v8.9, respectivamente) e posso fazer:

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

Você pode confirmar que primeiro teve que definir o "downlevelIteration": trueseu tsconfig.json?
Ahmed Fasih

3
Eu não tenho downlevelIteratondefinido, meu objetivo é es2017no entanto.
Lazieburd

Não posso fazer isso para o elemento Map <string, CustomClass []>? O compilador diz que não é um array de tipo ou tipo de string.
Rajashree Gr

isso não funciona para mim no 2.8 - eu receboType 'Map<K, V>' is not an array type or a string type.
Simon_Weaver

3

Você também pode aplicar o método de mapa de matriz ao iterável Map.entries ():

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

Além disso, conforme observado em outras respostas, pode ser necessário ativar a iteração de nível inferior no tsconfig.json (nas opções do compilador):

  "downlevelIteration": true,

Esse recurso foi introduzido no TypeScript 2.3. O problema ocorreu com o TypeScript 1.8.10
mwe

Se você não pode usar "downlevelIteration", pode usar:const projected = Array.from(myMap).map(...);
Efrain

1

Apenas uma explicação simples para usá-lo em um documento HTML.

Se você possui um Mapa de tipos (chave, matriz), inicializa a matriz da seguinte maneira:

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

E, para iterar, você cria uma matriz a partir dos valores-chave.

Basta usá-lo como uma matriz como em:

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

Então, em HTML, você pode usar:

*ngFor="let key of keys"

Dentro desse loop, você apenas obtém o valor do array com:

this.cityShop.get(key)

Feito!


1

No TypeScript 3.5 e Angular 8 LTS, era necessário converter o tipo da seguinte maneira:

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

-1

Se você realmente não gosta de funções aninhadas, também pode percorrer as teclas:

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

Observe que você precisa filtrar as iterações que não são de chave com hasOwnProperty, se não fizer isso, receberá um aviso ou erro.


como iterar sobre o mapa em html? parece não funcionar de todo. <div ng-repeat = "(chave, valor) em model.myMap"> {{key}}. </div>
Shinya Koizumi,

@ powerfade917 Não funciona, funciona apenas para matrizes porque angular é uma pilha de lixo. Mas faça isso como uma nova pergunta e assim você aprenderá que angular não é uma pilha de lixo, mas você deve convertê-lo em uma matriz. Observe que você também não é o topo da programação, porque parece incapaz de diferenciar entre angular e datilografado.
peterh - Restabelece Monica
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.