Iterating over Typescript Map
I'm Trying to Iterate over a Typescript Map but I Keep Getting Errors and I Could Not Find Any Solution Yet for Such a Trivial Problem. My Code Is: myMap: Map...
I'm trying to iterate over a typescript map but I keep getting errors and I could not find any solution yet for such a trivial problem.
My code is:
myMap : Map<string, boolean>;
for(let key of myMap.keys()) {
console.log(key);
}
And I get the Error:
Type 'IterableIteratorShim<[string, boolean]>' is not an array type or a string type.
Full Stack Trace:
Error: Typescript found the following errors:
/home/project/tmp/broccoli_type_script_compiler-input_base_path (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)
I'm using angular-cli beta5 and typescript 1.8.10 and my target is es5. Has anyone had this Problem?
13 Answers
You could use Map.prototype.forEach((value, key, map) => void, thisArg?) : void instead
Use it like this:
myMap.forEach((value: boolean, key: string) => {
console.log(key, value);
});
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];
}
Just use Array.from() method to convert it to an Array:
myMap : Map<string, boolean>;
for(let key of Array.from( myMap.keys()) ) {
console.log(key);
}
This worked for me. TypeScript Version: 2.8.3
for (const [key, value] of Object.entries(myMap)) {
console.log(key, value);
}
Using Array.from, Array.prototype.forEach(), and arrow functions:
Iterate over the keys:
Array.from(myMap.keys()).forEach(key => console.log(key));
Iterate over the values:
Array.from(myMap.values()).forEach(value => console.log(value));
Iterate over the entries:
Array.from(myMap.entries()).forEach(entry => console.log('Key: ' + entry[0] + ' Value: ' + entry[1]));
Per the TypeScript 2.3 release notes on "New --downlevelIteration":
for..of statements, Array Destructuring, and Spread elements in Array, Call, and New expressions support Symbol.iterator in ES5/E3 if available when using--downlevelIteration
This is not enabled by default! Add "downlevelIteration": true to your tsconfig.json, or pass --downlevelIteration flag to tsc, to get full iterator support.
With this in place, you can write for (let keyval of myMap) {...} and keyval's type will be automatically inferred.
Why is this turned off by default? According to TypeScript contributor @aluanhaddad,
It is optional because it has a very significant impact on the size of generated code, and potentially on performance, for all uses of iterables (including arrays).
If you can target ES2015 ("target": "es2015" in tsconfig.json or tsc --target ES2015) or later, enabling downlevelIteration is a no-brainer, but if you're targeting ES5/ES3, you might benchmark to ensure iterator support doesn't impact performance (if it does, you might be better off with Array.from conversion or forEach or some other workaround).
This worked for me.
Object.keys(myMap).map( key => {
console.log("key: " + key);
console.log("value: " + myMap[key]);
});
I'm using latest TS and node (v2.6 and v8.9 respectively) and I can do:
let myMap = new Map<string, boolean>();
myMap.set("a", true);
for (let [k, v] of myMap) {
console.log(k + "=" + v);
}
You can also apply the array map method to the Map.entries() iterable:
[...myMap.entries()].map(
([key, value]: [string, number]) => console.log(key, value)
);
Also, as noted in other answers, you may have to enable down level iteration in your tsconfig.json (under compiler options):
"downlevelIteration": true,
On Typescript 3.5 and Angular 8 LTS, it was required to cast the type as follows:
for (let [k, v] of Object.entries(someMap)) {
console.log(k, v)
}
Just a simple explanation to use it in an HTML document.
If you have a Map of types (key, array) then you initialise the array this way:
public cityShop: Map<string, Shop[]> = new Map();
And to iterate over it, you create an array from key values.
Just use it as an array as in:
keys = Array.from(this.cityShop.keys());
Then, in HTML, you can use:
*ngFor="let key of keys"
Inside this loop, you just get the array value with:
this.cityShop.get(key)
Done!
I tried using Array.from( myMap.keys() ) to get an array of keys in my VSCode extension that's implemented using node.js, but it did not work, even after adding "downlevelIteration": true to my tsconfig.json with the target set to es2017 as suggested by Ahmed Fasih and others.
What ultimately worked was Jason Slobotski's suggestion to use Object.keys:
let keys = Object.keys(myMap);
If you don't really like nested functions, you can also iterate over the keys:
myMap : Map<string, boolean>;
for(let key of myMap) {
if (myMap.hasOwnProperty(key)) {
console.log(JSON.stringify({key: key, value: myMap[key]}));
}
}
Note, you have to filter out the non-key iterations with the hasOwnProperty, if you don't do this, you get a warning or an error.