1

I've noticed that the Set in ES2015 does not implement a simple toJSON function, such as serializing to an array. Below is the implementation I came up with that does just that:

Object.defineProperty(Set.prototype, 'toJSON', {
    enumerable: false,
    value: function () {
        return [...this];
    }
});

Is there any reason why a Set does not serialize to an array?

Are there any edge cases where this override for toJSON is a bad idea?

Jim Buck
  • 2,383
  • 23
  • 42
  • Converting a set to an array is pretty straightforward (as you have shown), so I'm not sure there would be any benefits from an additional API method. However, if you are primarily asking for the context of JSON serialization, maybe https://esdiscuss.org/ would be a better venue to search / ask. Maybe it was for consistency: `toJSON` cannot be added to `Map`, so it wasn't added to `Set`. – Felix Kling Feb 16 '16 at 18:42
  • Consistency with `Map` would make sense! And thanks for the link @FelixKling, I have not seen that yet. – Jim Buck Feb 16 '16 at 18:51
  • Modifying global prototypes is never a good idea. You should only do this if you're intending to polyfill an existing specification. – loganfsmyth Feb 16 '16 at 19:42
  • With a true ES6 environment you can create your own subclass: `class SerializableSet extends Set { toJSON() { return [...this]; } }` – CodingIntrigue Feb 17 '16 at 08:29
  • @RGraham Subclassing it makes the most sense when you have access to the code that will be using `Map`s and `Set`s, since you will know what things you will stuff in there. – Jim Buck Feb 22 '16 at 14:54

1 Answers1

1

See this answer as to why there can't be a general toJSON case for Maps, and for similar reasons, Sets. Basically, keys and/or Set items can be anything, including objects and references to other things that can't be serialized into JSON (which, remember, is a specific format with specific, stricter rules than just "turn into intelligible data of another type"). What you want here is more like "toArray" anyhow. You method already works for that inline, as would Array.from(Set), I think.

But if you wanted to add this sort of method to the prototype for your own internal usage without risking possible problems if a similar (but not identical) method is ever added, you could use a Symbol key'd prop.

var toArray = Symbol('toArray');
Object.defineProperty(Set.prototype, toArray, {
    enumerable: false,
    value: function () {
        return [...this];
    }
});

var g = new Set();
g.add(9);
g[toArray]();//-> [9]

If you do that, then you are guaranteed to not cause problems with anything other than your own code, since only your code will have access to the toArray Symbol key that references that method.

Community
  • 1
  • 1
Dtipson
  • 1,564
  • 16
  • 21
  • FWIW, `toJSON` is supposed to return a value that can be serialized to JSON, it should not return JSON itself. And since a set can easily be converted to an array, there shouldn't be a technical reason for not providing `toJSON`. *"Basically, [...] Set items can be anything, including objects and references to other things that can't be serialized into JSON"* That's true for plain arrays and objects as well though, and they can easily be converted to JSON. – Felix Kling Feb 18 '16 at 21:35
  • They don't have native toJSON methods either though, I think the point being that if you want to create a value that can be serialized to JSON, you should create it depending on what specifically you want to handle and how: it's not necessary something you can set as a general method on complex values. Some people serialize Maps by converting them into pairs, for instance: http://www.2ality.com/2015/08/es6-map-json.html which can then handle stringifying simple objects as keys. But if you just have simple keys, then you can convert them to objects. All depends. – Dtipson Feb 18 '16 at 22:17
  • @Dtipson I do understand why `Map`s don't have a built-in `toJSON` method, but `Set`s don't have any excuse, so I agree with @Felix Kling. But on the other hand I would think that they should both return something that matches their constructor arguments (array for Set, pair array for maps), just as the article you linked to mentions. – Jim Buck Feb 22 '16 at 15:01
  • @Dtipson Using `Symbol`s to keep a "private" reference to the method is a great way to modify the original prototype without polluting it. Definitely a good use case for `Symbol`s. – Jim Buck Feb 22 '16 at 15:07