16

My REST controller expects request input of the following format, which it successfully converts to a Java object containing a Map and a String as parameters:

{ 
"myMap" : { 
    "key1": "value1", 
    "key2": "value2", 
    "key3": "value3"},
"myString": "string value"
}

I am getting my data from an html form like so:

var myMap = new Map();
var String = document.getElementById('String').value;
for (var i = 0 ; i<anArray.length ; i++){
    var input = document.getElementsByClassName('input_' + (i+1));
    for (var j = 0 ; j<3 ; j++){
        if (input[j].checked){
            myMap.set(input[j].name, input[j].id);
        }
    }
}

Basically, this code boils down to:

var myMap = new Map();
myMap.set("key1", "value1");
myMap.set("key2", "value2");
myMap.set("key3", "value3");

This results in a map containing {key1 => value1, key2 => value2, etc} and a String. I have been trying to turn this into a json string like so, but it doesn't seem to work:

var myJson = {};
myJson.myMap = myMap;
myJson.myString = myString;
var json = JSON.stringify(myJson);

However, I am ending up with the following string: `{"myMap":{},"String":"myString"}' . So I probably have to do something different to stringify a map, but nothing I try is working.

Can anyone help me out?

I.Brok
  • 319
  • 1
  • 2
  • 16
  • What is `repetitie` ? Also please provide a [mcve] – charlietfl Jun 24 '17 at 20:00
  • Sorry. I forgot to change it, I corrected it to myMap – I.Brok Jun 24 '17 at 20:01
  • 1
    People may land on this page when they instead really want: `const obj1 = Object.fromEntries(map1);`, which was the best answer for me. https://stackoverflow.com/a/55537385/470749 – Ryan May 18 '21 at 22:36

7 Answers7

14

You can write a short conversion function to make a map into an object that can be stringified.

console.clear()

function mapToObj(map){
  const obj = {}
  for (let [k,v] of map)
    obj[k] = v
  return obj
}

const myMap = new Map();
myMap.set("key1", "value1");
myMap.set("key2", "value2");
myMap.set("key3", "value3");

const myString = "string value"

const myJson = {};
myJson.myMap = mapToObj(myMap);
myJson.myString = myString;
const json = JSON.stringify(myJson);

console.log(json)

Here is a version that that presumably would work where Map exists but some other ES6 constructs do not (though, this seems like an editor settings issue).

console.clear()

function mapToObj(map){
  var obj = {}
  map.forEach(function(v, k){
    obj[k] = v
  })
  return obj
}

var myMap = new Map();
myMap.set("key1", "value1");
myMap.set("key2", "value2");
myMap.set("key3", "value3");

var myString = "string value"

var myJson = {};
myJson.myMap = mapToObj(myMap);
myJson.myString = myString;
var json = JSON.stringify(myJson);

console.log(json)
Bert
  • 80,741
  • 17
  • 199
  • 164
  • `myMap` is not defined within the function block and the `map` param of the function is not used. I think you meant to use `of map` and the code just runs because of the definition of `myMap` defined outside the function. – Juan Jun 24 '17 at 20:19
  • I have been trying this. However, IntelliJ keeps giving me an error saying "let are not supported by the current javascript version". Any way to fix that? – I.Brok Jun 24 '17 at 20:20
  • @I.Brok You have a version that supports Map but not let? Interesting. I'll convert it. – Bert Jun 24 '17 at 20:22
  • 1
    @I.Brok in IntelliJ go to settings -> languages -> javascript -> Ecmascript 6 from the dropdown. – Juan Jun 24 '17 at 20:23
  • @ Bert Evans and @Juan Thanks for the help! Changing the version to Ecmascript 6 did the trick. But still thank you for converting it Bert. It is always good to learn multiple ways. – I.Brok Jun 24 '17 at 20:38
13

Using ES6 syntax, and especially if you have nested maps (otherwise Idan Dagan's answer is simpler), you can use the JSON.stringify()'s second argument, the reducer, as follows:

JSON.stringify(myMap, (key, value) => (value instanceof Map ? [...value] : value));

Thaelvyn
  • 131
  • 1
  • 5
  • Perfect! I was trying to use the ES 2019 feature `Object.fromEntries(map)` but realized this is only useful if non-nested maps need to be compared. I have to deal with a nested map and you solution works like a charm! – Aljosha Koecher Nov 19 '22 at 17:49
  • i get an empty list as the result from this – Erik Aronesty May 01 '23 at 21:21
5

JSON stringification has the special behavior for objects, arrays and functions.

For example:

JSON.stringify( function(){} ); // output: undefind
JSON.stringify( [2,undefined,function(){}, 5] ); //output: "[2,null,null,5]"

One way for solving that is to define a toJSON() method for it that returns a JSON-safe version of the object.

var myJson = {};
myJson.toJson = function() {
    return {  /* what ever you need*/}
}

Second, and more simple, using ES6 syntax:

JSON.stringify([...myMap]);
Idan Dagan
  • 10,273
  • 5
  • 34
  • 41
3

You can use Array.prototype.reduce along with spread notation to tersely convert your map to the format you need.

var myMap = new Map();
myMap.set("key1", "value1");
myMap.set("key2", "value2");
myMap.set("key3", "value3");
var myString = "string value";

function mapToObj (map) {
  return [...map].reduce((acc, val) => {
    acc[val[0]] = val[1];
    return acc;
  }, {});
}

const json = JSON.stringify({
  myMap: mapToObj(myMap),
  myString: myString
});

console.log(json);
korona
  • 2,308
  • 1
  • 22
  • 37
2

This hack will do the job (but it's a global override and must be used carefully):

  Map.prototype.toString = function() {

    let result = {};

    this.forEach((key, value) => { result[key] = value;});

    return JSON.stringify(result);
  };
Agustí Sánchez
  • 10,455
  • 2
  • 34
  • 25
0

Instead of a Map you can initialize myMap as follows

myMap = {};

Set elements in it as -

if (input[j].checked){
        myMap[input[j].name] = input[j].id;
}

Finally while converting you can do the following -

myJSON = {};
myJSON.myMap = JSON.stringify(myMap);
myJSON.myString = "StringValue";
var json = JSON.stringify(myJSON);
Rajeev Ranjan
  • 3,588
  • 6
  • 28
  • 52
  • Thanks a bunch. This also works fine. I had tried it before, but made the mistake of using `myMap.[input[j].name]` . Now all I need to consider which way I suits best and whether I really need it to be a Map, or whether it's fine to let it be an Object. If anything, I understand that difference between Map and Object better now, and that is always a good thing. – I.Brok Jun 24 '17 at 20:40
0

This is the best way in es6

JSON.stringify(Object.fromEntries(atlasIdById))
Dinuka Thilanga
  • 4,220
  • 10
  • 56
  • 93