1

It seems that socket.io is converting a set into an empty object when sending from a node js back end to the front end. Why would this be? Is it a limitation of JSON, bug in socket, or bug in node js?

On the front end I'm just doing something like socket.on('room', (data) => console.log(data));

In my node server I've tried this:

const set = new Set;
set.add("Test");
console.log(set); // outputs as: Set { 'Test' }
mySocketIoNameSpace.emit('room', set); // ends up on the front end as an empty object {}

...which doesn't work. Whereas this works fine:

const arr = new Array();
arr.push("Test");
mySocketIoNameSpace.emit('room', arr); // front end gets: ["Test"]

const obj = new Object();
obj["Test"] = true;
mySocketIoNameSpace.emit('room', obj); // front end gets: {Test: true}

I figure I will have to emit something like Array.from(set), or just use an array or object rather than a set, which seems unfortunate since it would detract from the set's speed or the uniqueness.

Luke
  • 18,811
  • 16
  • 99
  • 115
  • You would have to send an array, then you convert it back to a Set – Andrew Li Jul 14 '18 at 03:57
  • JSON has no way to represent a Set, WeakSet, Map, or WeakMap, thus when constructing a JSON string using variables of those types it will convert them to Objects. – Derek Jul 14 '18 at 05:04

2 Answers2

3

Node.js sends a JSON. If you convert Set to a JSON, it ignores it's values as they are not stored as properties. So you either convert it to array, or can use something like:

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

Code snippet from @tanguy_k post

Julius Dzidzevičius
  • 10,775
  • 11
  • 36
  • 81
  • 3
    or simply just do `mySocketIoNameSpace.emit('room', [...set]);` +1 tho for answer – Nick Parsons Jul 14 '18 at 04:21
  • 1
    @yourFather - Given your username I'm quite surprised you didn't begin with an introduction: "Luke, I am yourFather". – Luke Jul 16 '18 at 20:53
1

Sending objects from server to client is generally done via JSON and that is what socket.io's .emit() will try to do if you pass it any type of object.

But, JSON does not have a way to represent a Set object or any of the other newer Javascript collection objects like Map, WeakMap, etc...

Fortunately, a Set is really only a flat collection of keys and that can be represented directly as an array. The recipient can either use it as an array themselves or they can convert it to a Set when they receive it.

So, to just send it as an array, you can do this:

 mySocketIoNameSpace.emit('room', [...set]); 

And, the recipient, can just convert it to a Set like this:

socket.on('room', function(data) {
    if (Array.isArray(data)) {
        data = new Set(data);
    }
});

The reason that what you first tried:

mySocketIoNameSpace.emit('room', set);

does not work is because socket.io just passes the set variable directly a JSON converter and the JSON converter (with no special knowledge of a Set object) just sees it as a regular Javascript object with no enumerable properties, thus it has nothing to put in the JSON except an empty object. If you were to list the enumerable properties of a Set object, you would not find any.

jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • So the short-coming is mainly due to JSON, but also due the the JSON converter. Seems to me that a smart ES6 JSON converter would convert a set into an array rather than an object. – Luke Jul 16 '18 at 20:51
  • @Luke - Well, it's because there's no exact representation of the newer ES6 collections in the JSON format. JSON is its own specification, separate from Javascript. Yes, it does seem like it would be natural to represent a Set as an Array in JSON, but in the current JSON format, there is no way for the recipient to know that Array was supposed to be a Set when it is parsed on the other end so you have to add a tiny bit of smarts on both ends to your own code to do that. – jfriend00 Jul 16 '18 at 22:10