I’m working in JavaScript. I’d like to store a list of unique, unordered string values, with the following properties:
- a fast way to ask ‘is A in the list’?
- a fast way to do ‘delete A from the list if it exists in the list’
- a fast way to do ‘add A to the list if it is not already present’.
What I really want is a set. Any suggestions for the best way to mimic a set in JavaScript?
This question recommends using an Object, with the keys storing properties, and the values all set to true: is that a sensible way?
If you are programming in an ES6-capable environment (such as node.js, a specific browser with the ES6 capabilities you need or transpiling ES6 code for your environment), then you can use the
Setobject built into ES6. It has very nice capabilities and can be used as is right in your environment.For many simple things in an ES5 environment, using an Object works very well. If
objis your object andAis a variable that has the value you want to operate on in the set, then you can do these:Initialization code:
Question 1: Is
Ain the list:Question 2: Delete ‘A’ from the list if it’s there:
Question 3: Add ‘A’ to the list if it wasn’t already there
For completeness, the test for whether
Ais in the list is a little safer with this:because of potential conflict between built-in methods and/or properties on the base Object like the
constructorproperty.Sidebar on ES6: The current working version of ECMAScript 6 or somethings called ES 2015 has a built-in Set object. It is implemented now in some browsers. Since browser availability changes over time, you can look at the line for
Setin this ES6 compatibility table to see the current status for browser availability.One advantage of the built-in Set object is that it doesn’t coerce all keys to a string like the Object does so you can have both 5 and "5" as separate keys. And, you can even use Objects directly in the set without a string conversion. Here’s an article that describes some of the capabilities and MDN’s documentation on the Set object.
I have now written a polyfill for the ES6 set object so you could start using that now and it will automatically defer to the built-in set object if the browser supports it. This has the advantage that you’re writing ES6 compatible code that will work all the way back to IE7. But, there are some downsides. The ES6 set interface takes advantage of the ES6 iterators so you can do things like
for (item of mySet)and it will automatically iterate through the set for you. But, this type of language feature cannot be implemented via polyfill. You can still iterate an ES6 set without using the new ES6 languages features, but frankly without the new language features, it isn’t as convenient as the other set interface I include below.You can decide which one works best for you after looking at both. The ES6 set polyfill is here: https://github.com/jfriend00/ES6-Set.
FYI, in my own testing, I’ve noticed that the Firefox v29 Set implementation is not fully up-to-date on the current draft of the spec. For example, you can’t chain
.add()method calls like the spec describes and my polyfill supports. This is probably a matter of a specification in motion as it is not yet finalized.Pre-Built Set objects: If you want an already built object that has methods for operating on a set that you can use in any browser, you can use a series of different pre-built objects that implement different types of sets. There is a miniSet which is small code that implements the basics of a set object. It also has a more feature rich set object and several derivations including a Dictionary (let’s you store/retrieve a value for each key) and an ObjectSet (let’s you keep a set of objects – either JS objects or DOM objects where you either supply the function that generates a unique key for each one or the ObjectSet will generate the key for you).
Here’s a copy of the code for the miniSet (most up-to-date code is here on github).