JSON stringify a Set

JavascriptJsonEcmascript 6

Javascript Problem Overview


How would one JSON.stringify() a Set?

Things that did not work in Chromium 43:

var s = new Set(['foo', 'bar']);

JSON.stringify(s); // -> "{}"
JSON.stringify(s.values()); // -> "{}"
JSON.stringify(s.keys()); // -> "{}"

I would expect to get something similar to that of a serialized array.

JSON.stringify(["foo", "bar"]); // -> "["foo","bar"]"

Javascript Solutions


Solution 1 - Javascript

JSON.stringify doesn't directly work with sets because the data stored in the set is not stored as properties.

But you can convert the set to an array. Then you will be able to stringify it properly.

Any of the following will do the trick:

JSON.stringify([...s]);
JSON.stringify([...s.keys()]);
JSON.stringify([...s.values()]);
JSON.stringify(Array.from(s));
JSON.stringify(Array.from(s.keys()));
JSON.stringify(Array.from(s.values()));

Solution 2 - Javascript

You can pass a "replacer" function to JSON.stringify:

const fooBar = {
  foo: new Set([1, 2, 3]),
  bar: new Set([4, 5, 6])
};

JSON.stringify(
  fooBar,
  (_key, value) => (value instanceof Set ? [...value] : value)
);

Result:

"{"foo":[1,2,3],"bar":[4,5,6]}"

toJSON is a legacy artifact, and a better approach is to use a custom replacer, see https://github.com/DavidBruant/Map-Set.prototype.toJSON/issues/16

Solution 3 - Javascript

While all of the above work I suggest that you subclass set and add a toJSON method to make sure that it stringify's correctly. Especially if you are going to be stringifying often. I use sets in my Redux stores and needed to make sure this was never a problem.

This is a basic implementation. Naming is just to illustrate the point pick your own style.

class JSONSet extends Set {
    toJSON () {
        return [...this]
    }
}

const set = new JSONSet([1, 2, 3])
console.log(JSON.stringify(set))

Solution 4 - Javascript

The problem with all the previous approaches is that they all convert the set into Array, which is missing the entire point of Set and indexes.

What you should do is to use an Object instead. Either convert it with the following function or simply create it as Object instead of Set.

const mySet = new Set(['hello', 'world']);
const myObj = {};
for (let value of mySet.values()) {
  myObj[value] = true;
}

Then instead of using mySet.has('hello') Do myObj.hasOwnProperty('hello').

Then stringify it as an object without a problem.

Note: The following method uses more memory because it needs to store the value as well as the key. But performence wise it's still O(1) compared to Array.includes() which is O(n) and miss the point of even using a Set.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMitMaroView Question on Stackoverflow
Solution 1 - JavascriptOriolView Answer on Stackoverflow
Solution 2 - Javascripttanguy_kView Answer on Stackoverflow
Solution 3 - JavascriptStephen BoltonView Answer on Stackoverflow
Solution 4 - Javascripteliezra236View Answer on Stackoverflow