loading...

A cautionary note about use of Javascript's Set and Map objects

rgeraldporter profile image Rob Porter ・2 min read

A few weeks ago I was working on a really odd bug. An object was being assembled that contained some user data, and when we went to send that object up to the server as JSON, it was always incomplete -- it was missing data in one of the properties.

I tried all kinds of things to track down what was causing this: was an object merge acting unexpectedly? Was there an event somewhere that removed this data that I didn't know about?

It was actually much simpler than this. The problem, was JSON.stringify() and a Set contained within the object being stringified.

For an example, I'll create a user with some "lucky numbers", which I'm just making up as an example where the entries should be unique, so a Set would (in theory) be better than an Array.

const userData = {
    name: 'Haleema Greer',
    id: 7252,
    luckyNumbers: new Set([55, 45, 62, 21])
};

Looks good enough. Now, let's convert to JSON so we can send this to a server.

JSON.stringify(userData);

// > "{\"name\":\"Haleema Greer\",\"id\":7252,\"luckyNumbers\":{}}"

Wait, what just ate our luckyNumbers?

Apparently Set data just isn't in a format that can convert to JSON without some extra work.

One good solution is presented on Stack Overflow.

function Set_toJSON(key, value) {
  if (typeof value === 'object' && value instanceof Set) {
    return [...value];
  }
  return value;
}


JSON.stringify(userData, Set_toJSON);

// > "{\"name\":\"Haleema Greer\",\"id\":7252,\"luckyNumbers\":[55,45,62,21]}"

Much better!

Many guides will either state or imply Set can be used as a drop-in for Array, but they do not act the same and can have some unexpected consequences if you treat them the same.

Be aware these two types are different, and what works for one, may not work for the other.

Map also shares this issue as well.

const userDataMap = {
    name: 'Haleema Greer',
    id: 7252,
    luckyNumbers: new Map([
        [1, 55],
        [2, 45],
        [3, 62],
        [4, 21]
    ])
};

JSON.stringify(userDataMap);

// > "{\"name\":\"Haleema Greer\",\"id\":7252,\"luckyNumbers\":{}}"

Fixing this isn't as straight-forward, because Map's structure doesn't directly translate to JSON. It will be a matter of how you prefer to solve the problem.

For various solutions to converting Map to JSON, see this Stack Overflow discussion.

Posted on by:

Discussion

markdown guide