How to serialize a Map in javascript?

So...there is this type in js called Map and it is really nice...it is faster than an Object for iterations and calculations so I really like it. However...you can't pass Maps around as you could with objects.

I know that I could turn Map into JSON but it is costly to do so and it kind of looses the point of using Maps in the first place.

JSON format is not meant for Maps...it was meant for Objects.

So...lets move from the JSON format a little.

Is there a way for me to serialize a Map into a string in any way so that I can then do the opposite - from said serialized Map to end up with a Map

Preferably this method should be as easy to perform as JSON.stringify or its counterpart JSON.parse.

I want to use Map as it is faster and better but I need to send my data as string. The format of the string is not important as long as I can parse it back into a Map


Solution 1:

-- edit: Added the missing JSON Stringify function during serialization -

There is a native way of doing this in Javascript.

the Map object has a method called entries() that returns an iterator that will yield each entry in the map as an array with 2 values in it. It will look like [key, value].

To avoid writing any looping code yourself, you can use Array.from() which can consume an iterator and extract each item from it. Having that, the following will give you your Map object in a serialized form.

let mySerialMap = JSON.stringify(Array.from(myMap.entries()))
console.log(mySerialMap)

Now, that's not even the coolest part. But before getting there, let me show you one possible way of using the Map constructor.

let original = new Map([
  ['key1', 'the-one-value']
  ['second-key, 'value-two']
])

The array of array that you can see being passed to the Map object is in the same format as what you get from using Array.from(myMap.entries()).

So you can rebuild you map in a single line using the following sample code:

let myMap = new Map(JSON.parse(mySerialMap))

And you can use myMap as you would any Map.

let myMap = new Map(JSON.parse(mySerialMap))
let first = myMap.get('some-key')
myMap.set('another-key', 'some value')

Solution 2:

I guess the whole point of Maps/Dictionaries is that you can use objects as keys in them, so:

let a = {}, b = {}, m = new Map();

m.set(a,b);
m.get(a); // b

So you get b since you have a reference on a. Let's say you serialize the Map by creating an Array of arrays, and stringify that to json:

function serialize (map) {
  return JSON.stringify([...map.entries()])
}

let s = serialize(m); // '[[{}, {}]]'
                      // '[[<key>, <val>], … ]'

Than you could:

let m2 = JSON.parse(s).reduce((m, [key, val])=> m.set(key, val) , new Map());

But the question now is: How to get a certain key? Since there does not exist any reference, due to the fact that all objects have been stringified and recreated, it is not possible to query a dedicated key.

So all that would only work for String keys, but that really takes most of power of maps, or in other words, reduces them to simple objects, what is the reason maps were implemented.

Solution 3:

To @philipp's point, people who care about the serialization of the Map will probably prefer objects (leverages intuition, reduces '[]' arithmetic). Object.entries() and Object.fromEntries() can make that a bit more literate:

writeMe = new Map()
writeMe.set('a', [1])
writeMe.set('b', {myObjValue: 2})
// ▶ Map(2) {'a' => Array(1), 'b' => {…}}

written = JSON.stringify(Object.fromEntries(writeMe.entries()))
// '{"a":[1],"b":{"myObjValue":2}}'

read = new Map(Object.entries(JSON.parse(written)))
// ▶ Map(2) {'a' => Array(1), 'b' => {…}}

read.get("b")
// ▶ {myObjValue: 2}