243

For some reason I can't find this simple thing in the MDN docs (maybe I'm just missing it).

I expected this to work:

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

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

...but the first line throws TypeError: (var)[Symbol.iterator] is not a function

How do I make a Map from a plain object? Do I really have to first convert it into an array of arrays of key-value pairs?

callum
  • 34,206
  • 35
  • 106
  • 163
  • 4
    FWIW, it may be worth switching your accepted answer from [mine](https://stackoverflow.com/a/36644532/157247) to [nils'](https://stackoverflow.com/a/36644558/157247) or [bergi's](https://stackoverflow.com/a/36644965/157247). `Object.entries` really is the better approach over `Object.keys`, and bergi's generator function approach is slightly more direct than either `Object.keys` or `Object.entries`. – T.J. Crowder Jul 11 '18 at 12:30

6 Answers6

358

Yes, the Map constructor takes an array of key-value pairs.

Object.entries is a new Object static method available in ES2017 (19.1.2.5).

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

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

It's currently implemented in Firefox 46+ and Edge 14+ and newer versions of Chrome

If you need to support older environments and transpilation is not an option for you, use a polyfill, such as the one recommended by georg:

Object.entries = typeof Object.entries === 'function' ? Object.entries : obj => Object.keys(obj).map(k => [k, obj[k]]);
nils
  • 25,734
  • 5
  • 70
  • 79
38

Do I really have to first convert it into an array of arrays of key-value pairs?

No, an iterator of key-value pair arrays is enough. You can use the following to avoid creating the intermediate array:

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

const map = new Map(entries({foo: 'bar'}));
map.get('foo'); // 'bar'
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • Nice example - just a note to others, you might want to do a `if(!obj.hasOwnProperties(key)) continue;` right after the for loop condition to ensure that you do not yield properties inherited from the object prototype (unless you trust the object, but should do this anyways when iterating objects using `in` as a good habit). – puiu Mar 02 '17 at 20:16
  • 6
    @Puiu No, you should not, and it's a bad habit. If you don't trust the object, you must not trust its `.hasOwnProperty` property as well, and you'd have to use `Object.prototype.hasOwnProperty.call(obj, key)` – Bergi Mar 02 '17 at 20:26
  • @Bergi that's a good point, it could be the case that the object's`hasOwnProperty` has also been overwritten. Are you saying it's good practice then to not bother with checking? Just curious, all the conventions out there say to check or they suggest to avoid using for...in anyways. I'd like to hear your thoughts on this. – puiu Mar 02 '17 at 20:42
  • 5
    Yes, I think not bothering is the best practice. You should trust all the objects that are worth enumerating (i.e. key-value-maps) to not have any enumerable inherited properties. (And yes of course [avoid enumerating arrays](https://stackoverflow.com/q/500504/1048572)). If you have a rare case of enumerating something else, and you bother, you should at least do it properly with `call`. All the conventions out there that recommend `obj.hasOwnProperties(key)` appear to have no idea what they are doing. – Bergi Mar 02 '17 at 20:50
  • @Bergi thank you for the response, trust but verify (if needed :P) and don't abuse for-in by using it on non-objects – puiu Mar 02 '17 at 22:14
  • 4
    Converting an `Object` into a `Map` is an expensive operation and the OP specifically asked for a solution without intermediates. So why isn't this the excepted answer? Well, asking this is probably futile, it just annoys me. –  Oct 06 '17 at 13:22
  • In TypeScript, this prompts an error of `Overload 1 of 3, '(iterable: Iterable): Map', gave the following error. Argument of type 'Generator' is not assignable to parameter of type 'Iterable'.` Any idea of this means? – zenoh Aug 04 '20 at 23:36
  • 1
    @tmvnty You might need to spell out the type `function* entries(obj: T): Generator {…}`. Also `yield [key, obj[key]] as const;` would help TypeScript realise that it's yielding tuples, not arrays. – Bergi Aug 04 '20 at 23:49
  • The function signature specified in @Bergi's comment has an `any` return type for the generator. To avoid this, you'll want to specify `Generator` as the return value (adding `, void`). – brainbag Mar 07 '22 at 10:43
28

The answer by Nils describes how to convert objects to maps, which I found very useful. However, the OP was also wondering where this information is in the MDN docs. While it may not have been there when the question was originally asked, it is now on the MDN page for Object.entries() under the heading Converting an Object to a Map which states:

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 }
Andrew Willems
  • 11,880
  • 10
  • 53
  • 70
16

ES6

convert object to map:

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

convert map to object:

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

Note: the mapToObj function assumes map keys are strings (will fail otherwise)

Yair Levy
  • 1,354
  • 16
  • 12
8
const myMap = new Map(
    Object
        .keys(myObj)
        .map(
            key => [key, myObj[key]]
        )
)
Ohar
  • 297
  • 3
  • 15
4

Alternatively you can use the lodash toPairs method:

const _ = require('lodash');
const map = new Map(_.toPairs({foo: 'bar'}));
Maurits Rijk
  • 9,789
  • 2
  • 36
  • 53