Socket.io: socket.rooms is a useless object

Created on 8 Mar 2017  ·  8Comments  ·  Source: socketio/socket.io

This is concerning the socket.rooms object, documented as:

A hash of strings identifying the rooms this client is in, indexed by room name.

I don't know what that means, but the example is clear:

io.on('connection', function(socket){
  socket.join('room 237', function(){
    console.log(socket.rooms); // [ <socket.id>, 'room 237' ]
    io.to('room 237', 'a new user has joined the room'); // broadcast to everyone in the room
  });
});

When I run console.log(socket.rooms);, I _want_ that pretty array (so I can slice off and get just the rooms), but I get this ugly object back:

io.on('connection', function(socket){
  socket.join('room 237', function(){
    console.log(socket.rooms); // { <socket.id>: '<socket.id>', 'room 237': 'room 237' }
  });
});

... which I then have to turn into an array with .keys() and then pick out the socket.id by hand (since it might not be first).

Is this a bug? I'm using 1.7.3.

Most helpful comment

Yah this seems unnecessarily complicated. I'm really struggling to understand why the user's socket id constitutes being a "room"?, or why key:value pairs are needed. It just needs to be a list - whatever rooms are present, you are in, done.

['room1', 'room2', 'room3']

And as above, io.rooms to get them all, either, also as a string array, or in this case as an object with room names as keys and an array of socket ids as their value.

{ room1: ['id1', 'id2', 'id3'], room2: ['id4', 'id5', 'id6'] }

All 8 comments

Well, it seems the documentation is not up-to-date here, the rooms was actually an array until v1.4 I think.

Note: I guess we could use a boolean as value.

I guess this is a feature request then. Make socket.io great again: bring back the arrays!

My goal here is to get an array of rooms the client is in. With an array, I assume the socket.id would be first:

var rooms = socket.rooms.slice(1);

Using the current implementation:

var roomKeys = Object.keys(socket.rooms);
var socketIdIndex = roomKeys.indexOf( socket.id );
var rooms = roomKeys.splice( socketIdIndex, 1 );

If the socket.id weren't included, it would be OK too. I just don't want to have to have to find it and pick it out.

var rooms = Object.keys(socket.rooms);

With booleans, I would be able to filter out the true values, like this (right?):

var roomKeys = Object.keys(socket.rooms);
var rooms = roomKeys.filter(function(key){ 
  return socket.rooms[key]==true;
});

Which is just as slow as iterating with indexOf().

It _would_ be helpful to include rooms that the client isn't in (such as to show chat rooms it can join). I just think that information would be better elsewhere, like the Server instance:

var allRooms = io.rooms;
var myRooms = socket.rooms;

Anyone else have an opinion about this?

Yah this seems unnecessarily complicated. I'm really struggling to understand why the user's socket id constitutes being a "room"?, or why key:value pairs are needed. It just needs to be a list - whatever rooms are present, you are in, done.

['room1', 'room2', 'room3']

And as above, io.rooms to get them all, either, also as a string array, or in this case as an object with room names as keys and an array of socket ids as their value.

{ room1: ['id1', 'id2', 'id3'], room2: ['id4', 'id5', 'id6'] }

I think the main reason is the lookup performance.
For the example, when a user was joined 100 room it would be more faster to use object lookup to find out if he was joined a room rather than iterating array value.

Refer to my answer on stackoverflow

{
    "room1":"room1",
    "room2":"room2",
    ...
    "room100":"room100"
}

To check if user has joined a room just a simple like this:
if(socket.rooms[roomID]) return true;

If it's array type then we need to use indexOf or iterate every array:
if(socket.rooms.indexOf(roomID) != -1) return true;

But I totally agree if the the key value is changed to a boolean:
{ room1: true, ... , room100: true }

It may help saving some memory usage

I wouldn't mind changing socket.rooms so that the values are booleans. Is it okay to work on this? I'm not sure what the contributing process is as I didn't see a CONTRIBUTING.md file.

Wouldn't a Set make more sense?

if (socket.rooms.has(roomID)) return true;

The structure would be simply Set(4) {"<socket ID>", "room1", "room2", "room3"} for example

I agree as long as the set's time complexity is constant. I believe in javascript it is.

In Socket.IO v3, Socket#rooms will now be a Set, and use the value provided by the underlying adapter, see here.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

distracteddev picture distracteddev  ·  3Comments

adammw picture adammw  ·  4Comments

MyMomSaysIAmSpecial picture MyMomSaysIAmSpecial  ·  4Comments

karmac2015 picture karmac2015  ·  3Comments

kootoopas picture kootoopas  ·  4Comments