In one of my recent articles titled Preparing for ECMAScript 6: Map and WeakMap, I introduced you to two new data types available in ECMAScript 6: Map
and its weak counterparts WeakMap
. In this tutorial we’re going to cover another duo of similar data types called Set
and WeakSet
. They share a lot of similarities with Map
and WeakMap
, especially when it comes to the methods available. However, as we’ll discuss here, they have different scopes.
As I’ve pointed out in all the previous articles discussing ECMAScript 6, if you want to polyfill what we’ll cover, you can employ es6-shim by Paul Miller.
Set
Like the name says, the Set
data type represents a set of elements (a collection). As mathematical notion suggests, this means that a set lets you store the same elements only once (e.g. the string “test” can’t be stored twice). Like other JavaScript data types, it isn’t mandatory to store elements of the same type, so in the same set you can store arrays, numbers, strings, and so on.
It’s also worth noting that a single element in a set cannot be retrieved, for example using a get()
method. The reason is that an element has neither a key nor an index you can refer to in order to retrieve it. But because you can verify that an element is contained in a given Set
instance, you don’t need a get()
method. For example, if you know the string “test” is contained in a set you don’t need to retrieve it, because you already have that value. It’s still possible to retrieve all the elements stored, as you’ll learn in this tutorial.
“But when is this data type a good fit?” you may ask. Well, let’s say that you need to store the IDs of some elements. When it comes to these situations, you don’t want duplicates. Under these circumstances and in ECMAScript 5, most of you have probably used arrays or objects to store the elements. The problem is that every time a new element comes in, you have to check that it hasn’t been already added to avoid duplicates. If you used an array, you’d have code like this:
var collection = [1, 2, 3, 4, 5];
var newElements = [4, 8, 10];
for(var i = 0; i < newElements.length; i++) {
if (collection.indexOf(newElements[i]) === -1) {
collection.push(newElements[i]);
}
}
Using the Set
data type, you can simplify the previous code as shown below:
var collection = new Set([1, 2, 3, 4, 5]);
var newElements = [4, 8, 10];
for(var i = 0; i < newElements.length; i++) {
collection.add(newElements[i]);
}
Now that you know what Set
is and when to use it, let’s discuss the properties and the methods exposed.
Set.prototype.size
The size
property returns the number of elements in a Set
instance. This is similar to the length
of the Array
data type.
Set.prototype.constructor()
The constructor, as you might know, is used to instantiate new objects. It accepts an optional argument called iterable
that is an array or an iterable object whose elements will be added to the new set. A basic example of use is shown below:
var array = [1, 2, "test", {a: 10}];
var set = new Set(array);
Set.prototype.add()
The add()
method adds a new element to the set if it isn’t already present; otherwise the element isn’t added. The signature of this method is the following:
Set.prototype.add(value)
where value
is the element you want to store. This method modifies the set it’s called upon but also returns the new set, allowing for chaining. An example of how to use such feature is shown below:
var set = new Set();
set.add("test").add(1).add({});
This method is currently implemented in Firefox, Internet Explorer 11, Chrome 38 and Opera 25. In versions of Chrome prior to 38 and Opera prior to 25 this method is supported behind the activation of the flag “Enable Experimental JavaScript”.
Set.prototype.delete()
In the same way we can add elements, we can also delete them from a set. To do that we can use the delete()
method. It accepts the value to delete and returns true
if the element is successfully removed or false
otherwise. The signature of this method is shown below:
Set.prototype.delete(value)
value
represents the element you want to delete.
This method is currently implemented in Firefox, Internet Explorer 11, Chrome 38 and Opera 25. In versions of Chrome prior to 38 and Opera prior to 25 you have to activate the usual flag.
Set.prototype.has()
The has()
method is one of the methods that the Set
data type has in common with Map
. It allows us to verify if an element exists or not in the set. It returns true
if the value is found or false
otherwise. The signature of this method is as follows:
Set.prototype.has(value)
where value
is the value you want to search for.
This method is currently implemented in Firefox, Internet Explorer 11, Chrome 38 and Opera 25. In versions of Chrome prior to 38 and Opera prior to 25 this method is supported behind the activation of the flag “Enable Experimental JavaScript”.
Set.prototype.clear()
The clear()
method, like the one defined on Map
, is a convenient way to remove all the elements from a Set
instance. The method doesn’t have a return value (which means it returns undefined
). The signature of clear()
is shown below:
Set.prototype.clear()
clear()
is currently implemented in Firefox, Internet Explorer 11, Chrome 38 and Opera 25. In versions of Chrome prior to 38 and Opera prior to 25 you have to activate the usual flag.
Set.prototype.forEach()
Another method in common with Map
is forEach()
. We can use it to iterate over the elements stored in the set in insertion order. The signature of forEach()
is the following:
Set.prototype.forEach(callback[, thisArg])
callback
is a function to run on each of the elements in the set. The thisArg
parameter is used to set the context (this
) of the callback. callback
receives three parameters:
value
: the value of the element processedvalue
: the value of the element processedset
: theSet
object processed
As you can see, the value being processed is passed twice. The reason is to keep the method consistent with the forEach()
implemented in Map
and Array
.
This method is supported by Firefox, Internet Explorer 11, Chrome 38 and Opera 25. In versions of Chrome prior to 38 and Opera prior to 25 you have to activate the usual flag.
Set.prototype.entries()
The entries()
method enables us to obtain an Iterator
to loop though the set’s elements. The Iterator
contains an array of value
–value
pairs for each element in the set, in insertion order. The reason for this duplication is the same as before: to keep it consistent with the method of Map
. The signature of this method is:
Set.prototype.entries()
This method is currently supported by Firefox, Chrome 38 and Opera 25. In versions of Chrome prior to 38 and Opera prior to 25 you have to activate the usual flag.
Set.prototype.values()
Another method that belongs to this data type is values()
. It returns an Iterator
object containing the values of the elements of the set, in insertion order. Its signature is the following:
Set.prototype.values()
This method is currently supported by Firefox, Chrome 38 and Opera 25. In versions of Chrome prior to 38 and Opera prior to 25 this method is supported behind the activation of the flag “Enable Experimental JavaScript”.
Set.prototype.keys()
Curiously enough, Set
has also a keys()
method. It performs the same operation as values()
, so I won’t describe it.
WeakSet
WeakSet
is the weak counterpart to the Set
data type. A WeakSet
only accepts objects as its values. This means that {}
, function(){}
(functions inherit from Object
), and instances of your own classes are allowed, but "test"
, 1
, and other primitive data types are not.
The other important difference is that WeakSet
objects don’t prevent garbage collection if there aren’t any other references to an object stored (the reference is weak). Due to this difference, there aren’t any methods to retrieve values or more than one element at once such as Set.prototype.values()
and Set.prototype.entries()
. In addition, similarly to WeakMap
, there isn’t a size
property available.
As a final note, I want to highlight that Chrome 37 and Opera 24 support WeakSet
and its methods without a flag, while the same isn’t true for Set
. The newer version Chrome 38 and Opera 25 support Set
and its methods by default.
Putting it all together
Now that you’ve seen all the methods and properties of the Set
and the WeakSet
data types, it’s time to put them into action. In this section I’ve developed two demos so that you can play with these methods and have a better idea of their power. As you’ll note, I haven’t used the Set.prototype.keys()
method because I think it’s only good at confusing developers.
In the first demo I’ll use a Set
object and its methods except Set.prototype.keys()
.
// Creates a new Set object
var set = new Set();
// Defines an array will be stored in the set
var arr = [4, 1, 9];
// Adds a new Number to the set
set.add(1);
// Adds a new String to the set
set.add('Aurelio De Rosa');
// Adds a new Object to the set
set.add({name: 'John Doe'});
// Adds a new Array element to the set
set.add(arr);
// Checks whether the string "test" is stored in the set. Prints "false"
console.log(set.has('test'));
// Checks whether the number "1" is stored in the set. Prints "true"
console.log(set.has(1));
// Retrieves the set size. Prints "4"
console.log(set.size);
// Deletes the object {name: 'Aurelio De Rosa'}. Prints "false" because even if it has the same values and properties, it's a different object
console.log(set.delete({name: 'Aurelio De Rosa'}));
// Retrieves the set size. Prints "4"
console.log(set.size);
// Deletes the array arr. Prints "true" because it's the same array
console.log(set.delete(arr));
// Retrieves the set size. Prints "3"
console.log(set.size);
// Loops over each element of the set
set.forEach(function(value, samevalue, set) {
// Prints the value twice
console.log('Value ' + value + ' is the same as ' + samevalue);
});
var entries = set.entries();
var entry = entries.next();
// Loops over each element of the set
while(!entry.done) {
// Prints both the value and the key
console.log('Value ' + entry.value[1] + ' is the same as ' + entry.value[0]);
entry = entries.next();
}
var values = set.values();
var value = values.next();
// Loops over each value of the set
while(!value.done) {
// Prints the value
console.log('Value: ' + value.value);
value = values.next();
}
// Deletes all the elements in the set
set.clear();
// Retrieves the set size. Prints "0"
console.log(set.size);
A live demo of the previous code is shown below and also available as a JSFiddle.
In this second demo we’ll see how we can work with a WeakSet
object.
// Creates a new WeakSet object
var weakset = new WeakSet();
// Defines an object that will be stored in the set
var obj = {name: 'Aurelio De Rosa'};
// Adds an object to the set
weakset.add(obj);
// Adds a function to the set
weakset.add(function(){});
// Adds another object to the set
weakset.add({name: 'John Doe'});
// Checks whether the Object {name: 'John Doe'} exists in the weak set. Prints "false" because despite the fact that the passed object and the stored one have the same values and properties, they are different objects
console.log(weakset.has({name: 'John Doe'}));
// Checks whether the Object obj exists in the weak set. Prints "true" because it's the same object
console.log(weakset.has(obj));
// Deletes the obj element. Prints "true"
console.log(weakset.delete(obj));
// Deletes the function(){} element. Prints "false" because the passed function and the stored one they are different functions (objects)
console.log(weakset.delete(function(){}));
// Deletes all the elements of the weak set
weakset.clear();
A live demo of the previous code is shown below and also available as a JSFiddle.
Conclusion
In this tutorial I covered the new Set
and WeakSet
data types. In addition to Map
and WeakMap
they are the most interesting new types available in ECMAScript 6. I hope you enjoyed the article and learned something interesting.
I'm a (full-stack) web and app developer with more than 5 years' experience programming for the web using HTML, CSS, Sass, JavaScript, and PHP. I'm an expert of JavaScript and HTML5 APIs but my interests include web security, accessibility, performance, and SEO. I'm also a regular writer for several networks, speaker, and author of the books jQuery in Action, third edition and Instant jQuery Selectors.