Veverke
Veverke

Reputation: 11358

What are the differences between javascript's Set and a regular plain object?

In trying to solve this question, I learned about the existence of javascript's Set object. I then checked MDN's Set documentation, and the question I am asking here immediately popped up.

This debate started in the aforementioned question's answers comments. I found that this thread also contributes to the debate.

So far, what I myself could make out of the debate is:

Can anyone provide a definite conclusion, if any? Perhaps I should not be comparing the two... but I still think it is a legitimate question and that it is pretty likely that others will ask the same upon learning about Set.

In any case, what initially propelled me to ask this was to edit MDN's Set specs adding the conclusion obtained here. But after the development of the thread here I think I should not be doing it, and perhaps Set's specs should not indeed mention a thing about "what about plain objects ultimately providing similar functionality". At least whoever asks himself the question may still find it here in SO and enjoy the insights/contributions added here.

Upvotes: 4

Views: 1175

Answers (3)

Pointy
Pointy

Reputation: 413737

Set instances are objects. If you construct a Set, you're free to add properties to it just like an instance of Date or Array:

var s = new Set();
s.name = "Herman";
s.shoeSize = 12;
console.dir(s); // just like an Object!
s.add(17);
if (s.has(17)) alert("yup!"); // yup!
if (17 in s) alert("yup!"); // no alert here!

The behaviors exposed by the Set prototype provide a functionality that really has nothing to do with the nature of objects, other than that references to objects are among the sorts of values that can be used with that API. Adding or removing values from a set via that API has no effect on the ordinary properties of the set object.

If one wished to implement a facility like the Set facility using only ES5 facilities, you'd have to do something like maintain an array of values, or a set of arrays by value type perhaps. The .add() and .has() and other methods would have to impose the rules of set membership over that list of values, probably by searching. There are probably some optimizations that could be done to make the searching more efficient, but the ES6 runtime has advantages, most notably that the runtime internals have access to raw object reference values and other such things.

Upvotes: 2

jfriend00
jfriend00

Reputation: 707456

First off, here's a post with code that shows how to start to use a plain Javascript object for set-like behavior (with some limitations).

And, here's a set of objects (that work in ES5) for getting lots more set-like behavior.

And, here a partial ES6 polyfill for the Set object implemented in ES5.

If you study any of this code, you will see that a plain Javascript object falls far short of an ES6 Set in many ways that require significant additional coding to work around.


Here are a couple of those issues:

  1. Assigning an object as a key will not work properly because all objects convert to a string key of "[object Object]" so all objects would have the same key in your set-like Javascript object.

  2. Assigning a number as a key will convert to the string representation so keys like 4 and "4" will conflict.

The ES6 Set object does not have these limitations. Here's more discussion of these two issues:

If you look at this answer to a prior post and the code in that answer, you can see how an underlying Javascript object can be used as the lookup mechanism for set-like behavior. Without lots of other coding though, it has some limitations because a Javascript object requires a string as the lookup key. An ES6 Set does not. So, out of the box the ES6 Set supports objects as elements. Using a Javascript object as a poor man's set does not support objects as elements in the set.

This becomes the most material when you want to add an object to the plain Javascript object-based set.

// create a set-like object
var mySet = {};

// create an object and put it in the set
var myObj = {greeting: "hello"};
mySet[myObj] = true;

Because a Javascript object requires a string key, it will call myObj.toString() to get such a key. Without a custom override for the toString() method, that will come out as "[object Object]" which is not at all what you want. See here for a demo. It will appear to work for one object, but as soon as you have more than one object in the set or set the set for a different object, it won't work at all because all objects would get indexed with the same key.

With an actual ES6 set, on the other hand, it natively accepts and works with objects just fine - you don't have to do anything special.

If you want to see how you can mimic an ES6 set as closely as possible using a plain Javascript object as the lookup mechanism, please read this answer. Further info is located on github where you can see what as to be done to make a regular Javascript object-based set implementation support Javascript objects with this ObjectSet implementation. There's even an ES6 Set polyfill here that uses an underlying Javacript object as it's storage mechanism.


A second issue arises with Javascript object based set implementations which is also because of the string key requirement. If you do this:

var mySet = {};
mySet[4] = true;
mySet["4"] = true;

You will end up with only one item in the set. This is because mySet[4] = true; converts the 4 to a string "4" to use as the key. If you are only using strings in your set or only using numbers in the set, then this can be easily worked around, but if you have both strings and numbers in the set, then a javascript object-base set implementation does not distinguish between 4 and "4" so does not treat them as separate items in the set. An ES6 does make this distinction.

Again, it would be possible to work around this with more code. If you manually do the toString() conversion yourself to create the key, one can preprend to the key value some type information such that in the above example, the key for the number 4 becomes "num_4" instead of just "4". To prevent collisions with a string '"num_4"`, you have to also do the same for string types. And numbers aren't the only type with this issue. In fact, in this ES6 Set polyfill, you can see a unique key being generated here. That unique key then has to be regenerated and used to do any lookup in the set.

Upvotes: 3

Naeem Shaikh
Naeem Shaikh

Reputation: 15715

Sets don't take duplicates see this fiddle

var mySet = new Set();

mySet.add(1);
mySet.add(5);
mySet.add("some text");


console.log('after adding duplicate  values ')
console.log('size='+mySet.size ) // size is 3


mySet.add("some text"); // adding duplicate value
console.log('after adding duplucate  value ')
console.log('size='+mySet.size ) // size is still 3

Upvotes: 1

Related Questions