如何将普通对象转换为ES6 Map?

249
由于某些原因,在MDN文档中我找不到这个简单的东西(也许是我错过了它)。
我期望这样可以工作:
const map = new Map({foo: 'bar'});

map.get('foo'); // 'bar'

...但第一行抛出了TypeError:(var)[Symbol.iterator]不是一个函数

我该如何从纯对象创建一个Map?我真的必须先将它转换为键值对数组的数组吗?


4
说实话,也许你应该考虑将你所接受的答案从我的改成nils的或者bergi的。使用Object.entries确实比Object.keys更好,而且bergi的生成器函数方法比Object.keysObject.entries都要直接一些。 - T.J. Crowder
6个回答

364

是的,Map 构造函数接受一个键值对数组。

Object.entries 是一个新的 Object 静态方法,在 ES2017 (19.1.2.5) 中可用。

const map = new Map(Object.entries({foo: 'bar'}));

map.get('foo'); // 'bar'

它目前在Firefox 46+、Edge 14+和更新版本的Chrome中实现

如果您需要支持旧环境且无法进行转译,可以使用一个 polyfill,例如 georg 推荐的那个。

Object.entries = typeof Object.entries === 'function' ? Object.entries : obj => Object.keys(obj).map(k => [k, obj[k]]);

4
“Polyfill”(多功能填充)的实现非常简单:Object.entries = obj => Object.keys(obj).map(k => [k, obj[k]]),它的作用是将对象转换为键值对数组。 - georg
4
Object.entries 自 Node 7.x 起正式加入(无需使用标志),顺便提一下。 - Lee Benson
2
不仅在Node7中有Object.entries,它也是ES2017最终规范的一部分。因此,在我看来,这应该是被接受的答案。 - AnilRedshift
1
这会保留或强制任何键的顺序吗? - backdesk
2
很遗憾,它不会。 - Nemanja Milosavljevic
显示剩余4条评论

38

我真的需要先将它转换为一个键值对数组的数组吗?

不需要,只需要一个键值对数组的迭代器就足够了。您可以使用以下方法避免创建中间数组:

function* entries(obj) {
    for (let key in obj)
        yield [key, obj[key]];
}

const map = new Map(entries({foo: 'bar'}));
map.get('foo'); // 'bar'

6
不,你不应该这么做,这是一个不好的习惯。如果你不信任这个对象,你也不能信任它的.hasOwnProperty属性,你必须使用Object.prototype.hasOwnProperty.call(obj, key) - Bergi
@Bergi,你说得很好,对象的hasOwnProperty可能也被覆盖了。那么你是说不检查是一个好习惯吗?只是好奇,所有的约定都建议检查或者建议避免使用for...in。我想听听你的想法。 - puiu
5
是的,我认为不去打扰是最好的做法。你应该相信所有值得枚举的对象(即键值映射)没有任何可枚举的继承属性。(当然,要避免枚举数组)。如果你有一个罕见的枚举其他东西的情况,并且你感到困扰,你至少应该使用call来正确地完成它。所有那些推荐使用obj.hasOwnProperties(key)的惯例似乎都不知道自己在做什么。 - Bergi
4
将一个“Object”转换为“Map”是一个昂贵的操作,而且OP明确要求不使用中间变量来解决问题。那么,为什么这不是被接受的答案呢?好吧,问这个问题可能是徒劳的,这只会让我感到烦恼。 - user6445533
1
@tmvnty 你可能需要明确指出类型 function* entries<T>(obj: T): Generator<readonly [keyof T, T[keyof T]]> {…}。同时,yield [key, obj[key]] as const; 将有助于 TypeScript 意识到它正在产生元组而不是数组。 - Bergi
显示剩余4条评论

28
Nils的答案描述了如何将对象转换为映射,我觉得非常有用。然而,提问者也想知道这些信息在MDN文档中的位置。虽然当问题最初被提出时可能没有出现在那里,但现在它在Object.entries()的MDN页面上,在标题将对象转换为映射下述:

Converting an Object to a Map

The new Map() constructor accepts an iterable of entries. With Object.entries, you can easily convert from Object to Map:

const obj = { foo: 'bar', baz: 42 }; 
const map = new Map(Object.entries(obj));
console.log(map); // Map { foo: "bar", baz: 42 }

16

ES6

将对象转换为 Map:

const objToMap = (o) => new Map(Object.entries(o));

将地图转换为对象:

const mapToObj = (m) => [...m].reduce( (o,v)=>{ o[v[0]] = v[1]; return o; },{} )

注意: mapToObj函数假设映射键为字符串(否则会失败)


4
您可以使用以下代码将 Map 转换回对象:Object.fromEntries(map.entries()) - Etienne Martin

9
const myMap = new Map(
    Object
        .keys(myObj)
        .map(
            key => [key, myObj[key]]
        )
)

3
@Ohar和@TJCrowder的解决方案融合:var buildMap2 = o => new Map(Object.keys(o).map(k => [k, o[k]])); - 7vujy0f0hy
1
谢谢,我也需要对对象键进行排序! - scottwernervt

4

或者你可以使用lodash toPairs方法:

const _ = require('lodash');
const map = new Map(_.toPairs({foo: 'bar'}));

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接