98

So Ive got the following javascript which contains a key/value pair to map a nested path to a directory.

function createPaths(aliases, propName, path) {
    aliases.set(propName, path);
}

map = new Map();

createPaths(map, 'paths.aliases.server.entry', 'src/test');
createPaths(map, 'paths.aliases.dist.entry', 'dist/test');

Now what I want to do is create a JSON object from the key in the map.

It has to be,

paths: {
  aliases: {
    server: {
      entry: 'src/test'
    },
    dist: {
      entry: 'dist/test'
    }
  }
}

Not sure if there is an out of a box way to do this. Any help is appreciated.

2
  • I'd substract key name from that string with dots and later build an object from what I gathered. Commented May 25, 2016 at 12:59
  • 1
    I hate it when people don't declare variables and end up with globals. Also, remember that 'It is a common mistake to call a JSON object literal "a JSON object". JSON cannot be an object. JSON is a string format.' (From W3Schools) Commented Oct 21, 2021 at 1:28

6 Answers 6

206

Given in MDN, fromEntries() is available since Node v12:

const map1 = new Map([
  ['foo', 'bar'],
  ['baz', 42]
]);

const obj = Object.fromEntries(map1);
// { foo: 'bar', baz: 42 }

For converting object back to map:

const map2 = new Map(Object.entries(obj));
// Map(2) { 'foo' => 'bar', 'baz' => 42 }
Sign up to request clarification or add additional context in comments.

1 Comment

Keep in mind that Map can hold non-string keys, so you'll get an undesirable result on, say, new Map([[{x: 0, y: 1}, 42]])
37

just using 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 & destructuring assignment & for...of

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 autoConvertMapToObject = (map) => {
  const obj = {};
  for (const item of [...map]) {
    const [
      key,
      value
    ] = item;
    obj[key] = value;
  }
  return obj;
}

const obj = autoConvertMapToObject(map)

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

  1. ...spread & destructuring assignment & forEach

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 autoConvertMapToObject = (map) => {
  const obj = {};
  [...map].forEach(([key, value]) => (obj[key] = value));
  return obj;
}

const obj = autoConvertMapToObject(map)

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

refs

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/fromEntries

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment

https://2ality.com/2015/08/es6-map-json.html

Comments

22

I hope this function is self-explanatory enough. This is what I used to do the job.

/*
 * Turn the map<String, Object> to an Object so it can be converted to JSON
 */
function mapToObj(inputMap) {
    let obj = {};

    inputMap.forEach(function(value, key){
        obj[key] = value
    });

    return obj;
}


JSON.stringify(returnedObject)

1 Comment

ES6 - implmentation: mapToObj(inputMap) { const obj = {}; inputMap.forEach((value, key) =>{ obj[key] = value; }); return obj; }
15

You could loop over the map and over the keys and assign the value

function createPaths(aliases, propName, path) {
    aliases.set(propName, path);
}

var map = new Map(),
    object = {};

createPaths(map, 'paths.aliases.server.entry', 'src/test');
createPaths(map, 'paths.aliases.dist.entry', 'dist/test');

map.forEach((value, key) => {
    var keys = key.split('.'),
        last = keys.pop();
    keys.reduce((r, a) => r[a] = r[a] || {}, object)[last] = value;
});

console.log(object);

2 Comments

Don't forget to add JSON.stringify(object)! (OP wants a JSON object...)
there is no JSON object, just a string which respects javascript-object-notation. stringify - i think - wasn't op's problem.
2

Another approach. I'd be curious which has better performance but jsPerf is down :(.

var obj = {};

function createPaths(map, path, value)
{
	if(typeof path === "string") path = path.split(".");
	
	if(path.length == 1)
	{
		map[path[0]] = value;
		return;
	}
	else
	{
		if(!(path[0] in map)) map[path[0]] = {};
		return createPaths(map[path[0]], path.slice(1), value);
	}
}

createPaths(obj, 'paths.aliases.server.entry', 'src/test');
createPaths(obj, 'paths.aliases.dist.entry', 'dist/test');

console.log(obj);

Without recursion:

var obj = {};

function createPaths(map, path, value)
{
    var map = map;
    var path = path.split(".");
    for(var i = 0, numPath = path.length - 1; i < numPath; ++i)
    {
        if(!(path[i] in map)) map[path[i]] = {};
        map = map[path[i]];
    }
    map[path[i]] = value;
}

createPaths(obj, 'paths.aliases.server.entry', 'src/test');
createPaths(obj, 'paths.aliases.dist.entry', 'dist/test');
createPaths(obj, 'paths.aliases.dist.dingo', 'dist/test');
createPaths(obj, 'paths.bingo.dist.entry', 'dist/test');

console.log(obj);

var obj = {};

function createPaths(map, path, value)
{
    var map = map;
    var path = path.split(".");
    
    while(path.length > 1)
    {
        map = map[path[0]] = map[path.shift()] || {};
    }
    
    map[path.shift()] = value;
  
}

createPaths(obj, 'paths.aliases.server.entry', 'src/test');
createPaths(obj, 'paths.aliases.dist.entry', 'dist/test');
createPaths(obj, 'paths.aliases.dist.dingo', 'dist/test');
createPaths(obj, 'paths.bingo.dist.entry', 'dist/test');

console.log(obj);

5 Comments

jsPerf has been down for weeks.
In my console using console.time, your code took, on average, 0.023ms, whereas the accepted answer took, on average, 0.137ms.
@evolutionxbox—if the recursion was replaced with sequential code (perhaps needing a couple more lines of code) it would likely run faster again. Less code doesn't necessarily mean faster. :-)
@RobG very true. These were very crude performance tests. About 20 "runs" were performed on each.
@RobG: I know but recursion is so cool. :P I added an example without recursion.
1
var items = {1:"apple",2:"orange",3:"pineapple"};
let map = new Map(Object.entries(items)); //object to map
console.log(map);
const obj = new Object();
map.forEach((value,key)=> obj[key]=value);  // map to object
console.log(obj);

1 Comment

Both of these have been covered by existing answers already, I think?

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.