Solution 1:

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:

Use this JSON.stringify replacer:

(because 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)

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

Then:

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

Result:

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

Solution 3:

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:

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.