迭代 Typescript Map
Posted
技术标签:
【中文标题】迭代 Typescript Map【英文标题】:Iterating over Typescript Map 【发布时间】:2016-10-08 12:49:00 【问题描述】:我正在尝试迭代一个打字稿地图,但我不断收到错误,对于这样一个微不足道的问题,我还没有找到任何解决方案。
我的代码是:
myMap : Map<string, boolean>;
for(let key of myMap.keys())
console.log(key);
我得到了错误:
Type 'IterableIteratorShim' 不是数组类型或字符串类型。
全栈跟踪:
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)
我正在使用 angular-cli beta5 和 typescript 1.8.10,我的目标是 es5。 有人遇到过这个问题吗?
【问题讨论】:
从 github github.com/Microsoft/TypeScript/issues/…查看这个答案 我犯了一个愚蠢的错误,在JS中,不要做map["key"] = "value"
,而是map.set("key", "value")
【参考方案1】:
您可以改用Map.prototype.forEach((value, key, map) => void, thisArg?) : void
像这样使用它:
myMap.forEach((value: boolean, key: string) =>
console.log(key, value);
);
【讨论】:
刚刚碰到这个。看起来 TypeScript 不尊重 map 迭代的规范,至少根据 MDN 指定的 for-of 循环。.forEach
是次优的,因为你不能打破它,AFAIK
不知道为什么它的价值是关键。似乎倒退了。
如果我们找到了我们需要的东西,如何停止这个迭代过程?
@Samjones 在 foreach 中使用 return;
与在普通 for 循环中使用 continue
相同
@monamona 并且,请祈祷,break
的等价物是什么,因为 Samjones 明确提到 break.... 我知道的唯一方法就是拥有一个外部标记skipToEnd = false
,而不是break;
放入skipToEnd = true; return
并且在函数的最开始你必须添加if (skipToEnd) return;
...不是很优雅。【参考方案2】:
如果你不是很喜欢嵌套函数,你也可以遍历键:
myMap : Map<string, boolean>;
for(let key of myMap)
if (myMap.hasOwnProperty(key))
console.log(JSON.stringify(key: key, value: myMap[key]));
注意,您必须使用hasOwnProperty
过滤掉非键迭代,如果不这样做,您会收到警告或错误。
【讨论】:
如何在 html 中遍历地图?它似乎根本不起作用。 key。 @powerfade917 它不起作用,它仅适用于数组,因为 angular 是一堆垃圾。但是将此作为一个新问题提出,您将了解到,角度不是一堆垃圾,但您必须将其转换为数组。请注意,您也不是编程的佼佼者,因为您似乎无法区分 Angular 和 typescript。【参考方案3】:只需使用Array.from()
方法将其转换为Array
:
myMap : Map<string, boolean>;
for(let key of Array.from( myMap.keys()) )
console.log(key);
【讨论】:
转换映射是一项非常耗性能的操作,而不是解决本质上只是编译器隐藏语言核心部分的问题的正确方法。只需<any>
-cast 映射以使用 for-of 对其进行迭代。
当心:我认为上面的@Kilves 建议将 Map 转换为“any”是一种优雅的解决方法。当我这样做时,代码编译并运行没有任何抱怨,但 Map 实际上并没有被迭代——循环的内容从未执行过。这里提出的Array.from()
策略确实对我有用。
我也试过了,它对我也不起作用,考虑到它 是 ES6 的一部分并且应该在大多数浏览器上“正常工作”,这就更加愚蠢了。但我猜我们的 angular 霸主在 zone.js 中使用了一些神奇的 mumbo jumbo 让它不起作用,因为他们讨厌 ES6。叹息。
@Kilves foreach
不支持完全异步/等待
@Han 我知道,这就是为什么我说 for-of,而不是 foreach。【参考方案4】:
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];
【讨论】:
上面给出的es6版本没有在严格模式下编译。 这个问题是关于打字稿,而不是普通的js【参考方案5】:我正在使用最新的 TS 和节点(分别为 v2.6 和 v8.9),我可以做到:
let myMap = new Map<string, boolean>();
myMap.set("a", true);
for (let [k, v] of myMap)
console.log(k + "=" + v);
【讨论】:
您能否确认您首先必须在您的tsconfig.json
中设置"downlevelIteration": true
?
我没有设置downlevelIteraton
,但是我的目标是es2017
。
我不能为 MapType 'Map<K, V>' is not an array type or a string type.
这对我不起作用。我应该使用任何“lib”吗?【参考方案6】:
根据TypeScript 2.3 release notes on "New --downlevelIteration
":
for..of statements
、Array Destructuring 和Spread 元素在Array、Call 和New 表达式中支持ES5/E3 中的Symbol.iterator(如果在使用--downlevelIteration
时可用)
默认情况下不启用此功能!将"downlevelIteration": true
添加到您的tsconfig.json
,或将--downlevelIteration
标志传递给tsc
,以获得完整的迭代器支持。
有了这个,你可以写for (let keyval of myMap) ...
和keyval
的类型将被自动推断。
为什么默认关闭?根据 TypeScript 贡献者@aluanhaddad,
它是可选的,因为它对生成代码的大小有非常显着的影响,并可能对所有可迭代对象(包括数组)的使用性能产生影响。
如果您可以针对 ES2015("target": "es2015"
in tsconfig.json
或 tsc --target ES2015
)或更高版本,启用 downlevelIteration
是不费吹灰之力,但如果您针对 ES5/ES3,您可以进行基准测试以确保迭代器支持不会影响性能(如果影响,您可能会更好地使用Array.from
转换或forEach
或其他一些解决方法)。
【讨论】:
在使用 Angular 时启用此功能是否可行或危险?【参考方案7】:使用Array.from、Array.prototype.forEach() 和arrow functions:
遍历键:
Array.from(myMap.keys()).forEach(key => console.log(key));
遍历值:
Array.from(myMap.values()).forEach(value => console.log(value));
遍历条目:
Array.from(myMap.entries()).forEach(entry => console.log('Key: ' + entry[0] + ' Value: ' + entry[1]));
【讨论】:
不知道为什么,我有像 Map这对我有用。打字稿版本:2.8.3
for (const [key, value] of Object.entries(myMap))
console.log(key, value);
【讨论】:
我通过将 Object.entries(myMap) 更改为 myMap.entries() 来实现此功能。我喜欢这个答案,因为它避免了 .forEach 调用的错误处理陷阱。 值得注意的是,如果tsconfig
中的 target
是 es5
,则会引发错误,但 es6
可以正常工作。你也可以在定位es6
时只做for (const [key, value] of myMap)
【参考方案9】:
只是在 HTML 文档中使用它的简单说明。
如果你有一个类型(键、数组)的 Map,那么你可以这样初始化数组:
public cityShop: Map<string, Shop[]> = new Map();
要对其进行迭代,您可以从键值创建一个数组。
只需将其用作数组,如下所示:
keys = Array.from(this.cityShop.keys());
然后,在 HTML 中,您可以使用:
*ngFor="let key of keys"
在这个循环中,您只需通过以下方式获取数组值:
this.cityShop.get(key)
完成!
【讨论】:
【参考方案10】:这对我有用。
Object.keys(myMap).map( key =>
console.log("key: " + key);
console.log("value: " + myMap[key]);
);
【讨论】:
这样,键将始终是字符串【参考方案11】:您还可以将数组映射方法应用于 Map.entries() 迭代:
[...myMap.entries()].map(
([key, value]: [string, number]) => console.log(key, value)
);
此外,如其他答案中所述,您可能必须在 tsconfig.json 中启用下级迭代(在编译器选项下):
"downlevelIteration": true,
【讨论】:
这个功能是在 TypeScript 2.3 中引入的。 TypeScript 1.8.10 出现问题 如果不能使用“downlevelIteration”,可以使用:const projected = Array.from(myMap).map(...);
【参考方案12】:
在 Typescript 3.5 和 Angular 8 LTS 上,需要按如下方式转换类型:
for (let [k, v] of Object.entries(someMap))
console.log(k, v)
【讨论】:
以上是关于迭代 Typescript Map的主要内容,如果未能解决你的问题,请参考以下文章