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


126

由于某种原因,我无法在MDN文档中找到这个简单的东西(也许我只是想念它)。

我希望这可以工作:

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

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

...但是第一行抛出 TypeError: (var)[Symbol.iterator] is not a function

如何从普通对象制作地图?我真的必须首先将其转换为键值对数组的数组吗?


2
FWIW,可能值得将您接受的答案从我的改为nilsbergiObject.entries的确是更好的方法Object.keys,而bergi的生成器函数方法比Object.keys或都更直接Object.entries
TJ Crowder

Answers:


193

是的,Map构造函数采用键值对数组。

Object.entriesES2017(19.1.2.5)中可用的新的Object静态方法。

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]]);

3
一个“ polyfill”将是微不足道的:Object.entries = obj => Object.keys(obj).map(k => [k, obj[k]])
乔治

4
Object.entries在节点7.x的正确(无标志)登陆顺便说一句
李-本森

2
Node.7中的Object.entries不仅是ES2017最终规范的一部分。因此,这应该是公认的答案,IMO
AnilRedshift

1
@AnilRedshift-好吧,这就是生成器函数的答案,因为OP要求一种避免中介的解决方案。
TJ Crowder

2
不幸的是,事实并非如此。
Nemanja Milosavljevic,

78

使用生成器函数查看nils的答案Object.entries和/或bergi的答案尽管Object.entries在提出问题时尚不在规范中,但它是在第4阶段进行的,因此可以安全地进行填充和甚至在2016年4月使用(也就是)。(有关此阶段的更多信息。)生成器功能在ES2015中。OP明确要求避免中介,虽然生成器并没有完全避免中介,但它比下面的(或略)做得更好Object.enties

FWIW,使用Object.entries

  • 创建[name, value]要传递给的数组数组new Map
  • Map构造函数调用数组以得到一个迭代器上的功能; 数组创建并返回一个数组插入对象。
  • Map构造函数使用了迭代器对象获取的条目(该[name, value]阵列),并建立映射

使用生成器:

  • 作为调用generator函数的结果创建一个generator对象
  • Map构造函数调用生成器对象的函数从它那里得到一个迭代器; 生成器对象返回自身
  • Map构造函数使用生成器对象(作为一个迭代)来获得条目(在[name, value]阵列)和构建地图

因此:减少了一个中介(来自的数组Object.entries)。

但是,使用起来Object.entries更简单,并且在99.999%的时间内创建该阵列都不是问题。真的,无论是哪一个。但是它们都比下面的要好。:-)


原始答案:

要初始化Map,您可以使用任何将键/值对作为数组返回的迭代器,例如数组数组:

const map = new Map([
    ['foo', 'bar']
]);

没有从对象到地图的内置转换,但是很容易做到Object.keys

const map = new Map();
let obj = {foo: 'bar'};
Object.keys(obj).forEach(key => {
    map.set(key, obj[key]);
});

当然,您可以给自己一个辅助函数来处理该问题:

function buildMap(obj) {
    let map = new Map();
    Object.keys(obj).forEach(key => {
        map.set(key, obj[key]);
    });
    return map;
}

然后

const map = buildMap({foo: 'bar'});

或者这是一个看起来更轻巧的版本(那还是一回事吗?):

function buildMap(obj) {
    return Object.keys(obj).reduce((map, key) => map.set(key, obj[key]), new Map());
}

(是的,Map#set返回地图参考。有些人认为,这是一abusagereduce。)

或者,我们真的可以超越晦涩之处:

const buildMap = o => Object.keys(o).reduce((m, k) => m.set(k, o[k]), new Map());

不,我永远不会真正做到这一点。:-)


1
@Ohar和@TJCrowder解决方案的融合:var buildMap2 = o => new Map(Object.keys(o).map(k => [k, o[k]]));
7vujy0f0hy

17
参见new Map(Object.entries(object)) stackoverflow.com/a/36644558/798133
拉斐尔·泽维尔

应优先选择Object.entries答案
Kir

@Kir- 还是发电机,是的。看到我的编辑。
TJ Crowder

31

我真的必须首先将其转换为键值对数组的数组吗?

不,键值对数组的迭代器就足够了。您可以使用以下命令来避免创建中间数组:

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

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

很好的例子-给其他人的提示,您可能希望if(!obj.hasOwnProperties(key)) continue;在for循环条件之后立即执行操作,以确保不产生从对象原型继承的属性(除非您信任该对象,但是在使用进行对象迭代时应始终这样做)in作为一个好习惯)。
Puiu

2
@Puiu不,你不应该,这是一个坏习惯。如果您不信任该对象,那么您也必须也不信任其.hasOwnProperty属性,而必须使用Object.prototype.hasOwnProperty.call(obj, key)
Bergi

2
是的,我认为最好不要这样做。您应该信任所有值得枚举的对象(即键值映射)不具有任何可枚举的继承属性。(当然,请避免枚举array)。如果您极少列举其他事物,并且您很麻烦,则至少应使用正确进行call。推荐的所有约定obj.hasOwnProperties(key)似乎都不知道它们在做什么。
Bergi'3

3
Objecta 转换为a Map是一项昂贵的操作,OP特意要求没有中间体的解决方案。那么,为什么这不是例外的答案呢?好吧,问这可能是徒劳的,这让我很烦。

1
@tmvnty您可能需要拼写出类型function* entries<T>(obj: T): Generator<readonly [keyof T, T[keyof T]]> {…}。这也yield [key, obj[key]] as const;将有助于TypeScript认识到它产生的是元组,而不是数组。
Bergi

11

Nils的答案描述了如何将对象转换为地图,我发现这非常有用。但是,OP还想知道此信息在MDN文档中的何处。最初问问题时可能还没有出现,但现在位于“ 将对象转换为地图 ”标题下的Object.entries()的MDN页面上,该状态指出:

将对象转换为地图

new Map()构造函数接受一个可迭代的entries。使用Object.entries,您可以轻松地从转换ObjectMap

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

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

1
@Ohar和@TJCrowder解决方案的融合:var buildMap2 = o => new Map(Object.keys(o).map(k => [k, o[k]]));
7vujy0f0hy

谢谢,因为我也需要对对象键进行排序!
scottwernervt


0

ES6

将对象转换为地图:

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

将地图转换为对象:

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

注意:mapToObj函数假定地图键是字符串(否则将失败)


-1

借助一些JQuery,

const myMap = new Map();
$.each( obj, function( key, value ) {
myMap[key] = value;
});

3
在2019年,我觉得我们不应该通过jquery解决问题。
illcrx

jQuery仍在许多项目中,这不是一个不好的答案,只是不是最佳的。
16:37的boatcoder
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.