12

I've got a Map<string, string> variable in typescript:

let m = Map<string, string>().set('tag', 'v1');

I want to convert to json string representation:

'{"tag": "v1"}'

I've tried 3 different ways. First is to use m.toString(). Second is using JSON.stringify(m). Both returned {}. I've even tried to convert the Map to a javascript object first and then convert to string:

function MapToString(map): string {
  let ro = {};
  Object.keys(map).forEach( key => {
    ro[key] = map[key];
  });
  return JSON.stringify(ro);
}

s = MapToString(m);

This returned {} as well when I tried to print it in the console.

breezymri
  • 3,975
  • 8
  • 31
  • 65

6 Answers6

13

I eventually gave up on using es6 Map and switched to TSMap, on which tags.toJSON() works.

breezymri
  • 3,975
  • 8
  • 31
  • 65
11

Readable? No, but it works

JSON.stringify(
  Array.from(
    new Map().set('tag', 'v1').set('foo', 'bar').entries()
  )
  .reduce((o, [key, value]) => { 
    o[key] = value; 

    return o; 
  }, {})
)

Like @james-hay pointed out, you have a typo that probably makes the object empty

Lostfields
  • 1,364
  • 1
  • 12
  • 20
10

Although it does not directly answer your question: when I had the same problem, I reconsidered my use of Map and went for plain JavaScript objects. Since JSON always needs strings as keys, one of Map's main advantages (keys of any type) are lost anyway.

Another advantage of Map is that they allow a nice type signature, like Map<String, Array<String>>. But here TypeScript's "index signatures" provide exactly this for plain JavaScript objects and even allow a name for keys:

interface CityLists {
    [postcode: string]: Array<string>
};

This can be used almost like a Map (with different syntax, of course) and it directly converts to JSON even when nested. I think the latter is quite important when you convert an object tree to JSON where maps can be nested somewhere deep down in other arbitrary objects and arrays.

Alternatively, TypeScript also has the type Record<K, T> for this use-case: a plain object used as a typed map. In the above example, I could write:

let cityLists: Record<string, Array<string>>;
cityLists["capitals"] = ["Rome", "Paris", "Berlin"];
Robert Jack Will
  • 10,333
  • 1
  • 21
  • 29
4

Surprised this wasn't mentioned yet:

let m = new Map().set('tag', 'v1').set('foo', 'bar')

JSON.stringify(Object.fromEntries(m))

Seems to me the most readable solution.

Spenhouet
  • 6,556
  • 12
  • 51
  • 76
0

In Nativescript with lib es2015 I can do:

From Object to string:

// no need to import JSON
let fooInString = JSON.stringify(this.mapObject.subMap);

From string to Object:

// no need to import JSON
let fooInObject = JSON.parse(fooInString);

Happy Coding!

-1

While this seems to be a typo as indicated in other answers, this problem can occur when ES6 and vanilla JS are used together in the same project. JS won't recognise the ES6 map and convert it to an empty object "{}".

A quick workaround is to use a serialiser/deserialiser whenever persisting ES6 maps in a JS object

ES6 to JS

convertTSMapToJSObj(tsMap) {
    const jsObj = {};
    tsMap.forEach(function (value, key) {
        jsObj[key] = value;
    });
    return jsObj;
}

JS to ES6

convertJSObjToTSMap(jsObj) {
    const tsMap = new Map();
    const arrayOfMapEntries = new Map<any, any>(Object.entries(jsObj);
    for (const [key, value] of arrayOfMapEntries.entries()) {
        tsMap.set(key, value);
    }
    return tsMap;
}
Shubham Rana
  • 370
  • 1
  • 4
  • 12