Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Convert ES6 Map to Object Literal
let map = new Map();
map.set("a", 1);
map.set("b", 2);
map.set("c", 3);
let obj = Array.from(map).reduce((obj, [key, value]) => (
Object.assign(obj, { [key]: value }) // Be careful! Maps can have non-String keys; object literals can't.
), {});
console.log(obj); // => { a: 1, b: 2, c: 3 }
@lukehorvat
Copy link
Author

lukehorvat commented Apr 8, 2015

You can also do this inside a custom toJSON method defined for the Map instance, so that it automatically takes place whenever JavaScript's JSON.stringify function is called.

@MichaelWalker-git
Copy link

MichaelWalker-git commented May 9, 2018

Instead of converting the map -> array -> iterating over array / filling in new object

Could you do this?

const convertMapsToObjects = (mapInstance) => {
  const obj = {};
  for(let prop of map){
    obj[prop[0]] = prop[1];
  }
 return obj;
}

http://jsben.ch/mGhq1

Thanks for sharing!

@goldhand
Copy link

goldhand commented May 25, 2018

Major performance hit when you create a new object (with Object.assign) inside every iteration.

Because you're creating a new object literal when you call Array.reduce(fn, {}), you can safely mutate that accumulator object from within the reducer function.

This is WAYYYYY faster:

let obj = Array.from(map).reduce((obj, [key, value]) => {
  obj[key] = value;
  return obj;
}, {});

Unfortunately, you'll never know because gist doesn't send comment notifications... I hope someone finds this comment and it helps them. Email me if it does, so I can be notified this helped someone 😉

@changbowen
Copy link

changbowen commented Jun 4, 2018

I think with ES6 if you replace Array.from(map) with [...map.entries()], it's even faster :)
Could also be a one-liner:
let obj = [...map.entries()].reduce((obj, [key, value]) => (obj[key] = value, obj), {});

@IllusionElements
Copy link

IllusionElements commented Jun 4, 2018

This would be the best way

  const obj = map.entries().reduce((main, [key, value]) => ({...main, [key]: value,{})

@jlyonsmith
Copy link

jlyonsmith commented Jun 19, 2018

@IllusionElements almost has it. reduce only exists on Array, so you have to convert the map.entries() from an iterator first. And there are some brackets missing:

const obj = Array.from(map.entries()).reduce((main, [key, value]) => ({...main, [key]: value}), {})

Will do the trick. @changbowen has the right answer too if you want to use the spread operator instead of Array.from().

@gwillz
Copy link

gwillz commented Jul 23, 2018

Doing a spread operator in the loop {...main, [key]: value} is as inefficient as Object.assign() in the OP.
In contrast; obj[key] = value is simply modifying the reducer sum value, rather than recreating it on every loop.
The ES6 spread operator is indeed faster than Array.from(), but is still slower than the for of loop.

TL:DR; crude results below at 100,000 iterations of a 15 element map.

fromAssign: 1255.439ms
forOf: 109.349ms
fromReuse: 369.059ms
spreadReuse: 237.935ms
fromSpread: 4260.636ms
spreadSpread: 4094.880ms

@seromenho
Copy link

seromenho commented Apr 4, 2019

With for...of

const obj = {};
for (let [key, value] of map) {
  obj[key] = value;
}

@guzmanoj
Copy link

guzmanoj commented Dec 11, 2019

No spread & no Array.from

const newObject = theMap.keys.reduce((obj, key) => {
                  obj[key] = theMap.get(key);
                  return obj;
                }, {});

@varoot
Copy link

varoot commented Jan 31, 2020

You can also use forEach method

const obj = {};
map.forEach((value, key) => (obj[key] = value));

@broofa
Copy link

broofa commented Feb 22, 2020

@varoot provided the secret sauce.

const obj = Object.fromEntries(map.entries());

@varoot
Copy link

varoot commented Feb 23, 2020

But note that Object.fromEntries is ES2019 so it's not technically "ES6" and might need additional polyfill for Node or browsers.

@varoot provided the secret sauce.

const obj = Object.fromEntries(map.entries());

@lsironic
Copy link

lsironic commented Mar 4, 2020

@varoot

const obj = {};
map.forEach((value, key) => (obj[key] = value));

no need to return anything in forEach.
map.forEach((value, key) => {obj[key] = value});

@leebenson
Copy link

leebenson commented Apr 10, 2020

In Node 12+, just Object.fromEntries(map).

@snowyu
Copy link

snowyu commented Jul 21, 2020

Need to think about recurisive map:

const m = new Map()
m.set('submap', new Map())

@snowyu
Copy link

snowyu commented Jul 21, 2020

export function fromMap(map) {
  return Array.from
    ( map.entries()
    , ([ k, v ]) =>
        v instanceof Map
          ? { _mK: k, _mVal: fromMap (v) }
          : { _mK: k, _mVal: v }
    )
}

export function toMap(arr) {
  let m = new Map()
  arr.forEach(item => {
    let v = item._mVal;
    if (Array.isArray(v) && v[0] && v[0]._mK !== undefined) v = toMap(v)
    m.set(item._mK, v)
  })
  return m
}

@marcusflat
Copy link

marcusflat commented Aug 8, 2020

function fromMap(map) {
  let obj = {}
  for(let[k,v] of map) {
      v instanceof Map 
        ? obj[k] = fromMap(v)
        : obj[k] = v
  }
  return obj
}

@snowyu
Copy link

snowyu commented Aug 9, 2020

@marcusflat: It can not reverse the object to map if so.

@marcusflat
Copy link

marcusflat commented Aug 10, 2020

@marcusflat: It can not reverse the object to map if so.

function toMap(obj) {
  let map = new Map()
  for(let k of Object.keys(obj)) {
      obj[k] instanceof Object 
        ? map.set(k, toMap(obj[k]))
        : map.set(k, obj[k]);
  }
  return map
}

@snowyu
Copy link

snowyu commented Aug 12, 2020

@marculsflat: your scene is that all object are the Map. but not all objects are the map in some scene.

treeMap =  {
'filepath': {
  name: 'filebasename',
  path: 'filepath',
  stat: new Stat(), // not map
  children: new Map(...)
}

@xgqfrms
Copy link

xgqfrms commented Oct 1, 2020

ES6 ways

  1. Object.fromEntries
const log = console.log;

const map = new Map();
// undefined
map.set(`a`, 1);
// Map(1) {"a" => 1}
map.set(`b`, 2);
// Map(1) {"a" => 1, "b" => 2}
map.set(`c`, 3);
// Map(2) {"a" => 1, "b" => 2, "c" => 3}

// Object.fromEntries ✅
const obj = Object.fromEntries(map);

log(`\nobj`, obj);
// obj { a: 1, b: 2, c: 3 }
  1. ...spread & destructing assignment
const autoConvertMapToObject = (map) => {
  const obj = {};
  for (const item of [...map]) {
    const [
      key,
      value
    ] = item;
    obj[key] = value;
  }
  return obj;
}

const log = console.log;

const map = new Map();
// undefined
map.set(`a`, 1);
// Map(1) {"a" => 1}
map.set(`b`, 2);
// Map(1) {"a" => 1, "b" => 2}
map.set(`c`, 3);
// Map(2) {"a" => 1, "b" => 2, "c" => 3}

const obj = autoConvertMapToObject(map);
log(`\nobj`, obj);
// obj { a: 1, b: 2, c: 3 }

@BinodNagarkoti
Copy link

BinodNagarkoti commented May 10, 2021

Major performance hit when you create a new object (with Object.assign) inside every iteration.

Because you're creating a new object literal when you call Array.reduce(fn, {}), you can safely mutate that accumulator object from within the reducer function.

This is WAYYYYY faster:

let obj = Array.from(map).reduce((obj, [key, value]) => {
  obj[key] = value;
  return obj;
}, {});

Unfortunately, you'll never know because gist doesn't send comment notifications... I hope someone finds this comment and it helps them. Email me if it does, so I can be notified this helped someone 😉

this one helped me.

@vjpr
Copy link

vjpr commented Sep 9, 2021

If a value in the map can be an array of maps you need:

const toObject = (map = new Map) => {
  if (!(map instanceof Map)) return map
  return Object.fromEntries(Array.from(map.entries(), ([k, v]) => {
    if (v instanceof Array) {
      return [k, v.map(toObject)]
    } else if (v instanceof Map) {
      return [k, toObject(v)]
    } else {
      return [k, v]
    }
  }))
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment