-
-
Save lukehorvat/133e2293ba6ae96a35ba to your computer and use it in GitHub Desktop.
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 } |
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 😉
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), {});
This would be the best way
const obj = map.entries().reduce((main, [key, value]) => ({...main, [key]: value,{})
@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()
.
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
With for...of
const obj = {};
for (let [key, value] of map) {
obj[key] = value;
}
No spread & no Array.from
const newObject = theMap.keys.reduce((obj, key) => {
obj[key] = theMap.get(key);
return obj;
}, {});
You can also use forEach
method
const obj = {};
map.forEach((value, key) => (obj[key] = value));
@varoot provided the secret sauce.
const obj = Object.fromEntries(map.entries());
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());
const obj = {}; map.forEach((value, key) => (obj[key] = value));
no need to return anything in forEach.
map.forEach((value, key) => {obj[key] = value});
In Node 12+, just Object.fromEntries(map)
.
Need to think about recurisive map:
const m = new Map()
m.set('submap', new Map())
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
}
function fromMap(map) {
let obj = {}
for(let[k,v] of map) {
v instanceof Map
? obj[k] = fromMap(v)
: obj[k] = v
}
return obj
}
@marcusflat: It can not reverse the object to map if so.
@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
}
@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(...)
}
ES6 ways
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 }
...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 }
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.
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]
}
}))
}
Instead of converting the map -> array -> iterating over array / filling in new object
Could you do this?
http://jsben.ch/mGhq1
Thanks for sharing!