Chapter 6. Sets

So far, we have learned about sequential data structures such as arrays (lists), stacks, queues, and linked lists (and their variants). In this chapter, we will cover the data structure called a set.

A set is a collection of items that are unordered and consists of unique elements (meaning they cannot be repeated). This data structure uses the same math concept as of finite sets, but applied to a Computer Science data structure.

Let's take a look at the math concept of sets before we dive into the Computer Science implementation of it. In Mathematics, a set is a collection of distinct objects.

For example, we have a set of natural numbers, which consists of integer numbers greater than or equal to 0: N = {0, 1, 2, 3, 4, 5, 6, …}. The list of the objects within the set is surrounded by {} (curly braces).

There is also the null set concept. A set with no element is called a null set or empty set. For example, a set of prime numbers between 24 and 29. As there is no prime number (a natural number greater than 1 that has no positive divisors other than 1 and itself) between 24 and 29, the set will be empty. We represent an empty set as { }.

You can also imagine a set as an array with no repeated elements and with no concept or order.

In Mathematics, a set also has some basic operations such as union, intersection, and difference. We will also cover these operations in this chapter.

Creating a set

The current implementation of JavaScript is based on ECMAScript 5.1 (supported by modern browsers) published on June 2011. It contains the Array class implementation that we covered in earlier chapters. ECMAScript 6 (a work in progress, expected to be released in March 2015 at the time of writing this book) contains an implementation of the Set class.

Note

You can see the details of the ECMAScript 6 Set class implementation at https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set (or http://goo.gl/2li2a5).

The class we are going to implement in this chapter is based on the Set implementation of ECMAScript 6.

This is the skeleton of our Set class:

function Set() {
    var items = {};
}

A very important detail is that we are using an object to represent our set ( items ) instead of an array. But we could also use an array to do this implementation. Let's use an object to implement things a little bit differently and learn new ways of implementing data structures that are similar. And also, objects in JavaScript do not allow you to have two different properties on the same key, which guarantees unique elements in our set.

Next, we need to declare the methods available for a set (we will try to simulate the same Set class implemented in ECMAScript 6):

  • add(value): This adds a new item to the set.
  • remove(value): This removes the value from the set.
  • has(value): This returns true if the value exists in the set and false otherwise.
  • clear(): This removes all the items from the set.
  • size(): This returns how many elements the set contains. It is similar to the length property of the array.
  • values(): This returns an array of all the values of the set.

The has (value) method

The first method we will implement is the has(value) method. We will implement this method first because it will be used in other methods such as add and remove. We can see its implementation here:

this.has = function(value){
    return value in items;
};

As we are using an object to store all the values of the set, we can use JavaScript's in operator to verify that the given value is a property of the items object.

But there is a better way of implementing this method, which is as follows:

this.has = function(value){
    return items.hasOwnProperty(value);
};

All JavaScript objects have the hasOwnProperty method. This method returns a Boolean indicating whether the object has the specified property or not.

The add method

The next method we will implement is the add method:

this.add = function(value){
    if (!this.has(value)){
        items[value] = value; //{1}
        return true;
    }
    return false;
};

Given a value, we can check whether the value already exists in the set. If not, we add the value to the set (line {1}) and return true to indicate that the value was added. If the value already exists in the set, we simply return false to indicate that the value was not added.

Note

We are adding the value as key and value because it will help us search for the value if we store it as the key as well.

The remove and clear methods

Next, we will implement the remove method:

this.remove = function(value){
    if (this.has(value)){
        delete items[value]; //{2}
        return true;
    }
    return false;
};

In the remove method, we will verify that the given value exists in the set. If this is positive, we remove the value from the set (line {2}) and return true to indicate the value was removed; otherwise, we return false.

As we are using an object to store the items object of the set, we can simply use the delete operator to remove the property from the items object (line {2}).

To use the Set class, we can use the following code as an example:

var set = new Set();
set.add(1);
set.add(2);

Note

Just out of curiosity, if we output the items variable on the console (console.log) after executing the previous code, this will be the output in Google Chrome:

Object {1: 1, 2: 2}

As we can see, it is an object with two properties. The property name is the value we added to the set and its value as well.

If we want to remove all the values from the set, we can use the clear method:

this.clear = function(){
    items = {}; // {3}
};

All we need to do to reset the items object is assign it to an empty object again (line {3}). We could also iterate the set and remove all the values one by one using the remove method, but that is too much work as we have an easier way of doing it.

The size method

The next method we will implement is the size method (which returns how many items are in the set). There are three ways of implementing this method.

The first method is to use a length variable and control it whenever we use the add or remove method, as we used in the LinkedList class in the previous chapter.

In the second method, we use a built-in function from the built-in Object class in JavaScript (ECMAScript 5+):

this.size = function(){
    return Object.keys(items).length; //{4}
};

The Object class in JavaScript contains a method called keys that returns an array of all properties of a given object. In this case, we can use the length property of this array (line {4}) to return how many properties we have in the items object. This code will work only in modern browsers (such as IE9+, FF4+, Chrome5+, Opera12+, Safari5+, and so on).

The third method is to extract each property of the items object manually and count how many properties there are and return this number. This method will work in any browser and is the equivalent of the previous code:

this.sizeLegacy = function(){
    var count = 0;
    for(var prop in items) { //{5}
        if(items.hasOwnProperty(prop)) //{6}
            ++count; //{7}
    }
    return count;
};

So, first we iterate through all the properties of the items object (line {5}) and check whether that property is really a property (so we do not count it more than once—line {6}). If positive, we increment the count variable (line {7}) and at the end of the method we return this number.

Note

We cannot simply use the for-in statement and iterate through the properties of the items object and increment the count variable value. We also need to use the has method (to verify that the items object has that property) because the object's prototype contains additional properties for the object as well (properties are inherited from the base JavaScript Object class, but it still has properties of the object, which are not used in this data structure).

The values method

The same logic applies to the values method, using which we want to extract all the properties of the items object and return it as an array:

this.values = function(){
    return Object.keys(items);
};

This code will only work in modern browsers. As we are using Google Chrome and Firefox as testing browsers in this book, the code will work.

If we want code that can be executed in any browser, we can use the following code, which is equivalent to the previous code:

this.valuesLegacy = function(){
    var keys = [];
    for(var key in items){ //{7}
        keys.push(key); //{8}
    }
    return keys;
};

So, first we iterate through all the properties of the items object (line {7}), add them to an array (line {8}), and return this array.

Using the Set class

Now that we have finished implementing our data structure, let's see how we can use it. Let's give it a try and execute some commands to test our Set class:

var set = new Set();

set.add(1);
console.log(set.values()); //outputs ["1"]
console.log(set.has(1));   //outputs true
console.log(set.size());   //outputs 1

set.add(2);
console.log(set.values()); //outputs ["1", "2"]
console.log(set.has(2));   //true
console.log(set.size());   //2

set.remove(1);
console.log(set.values()); //outputs ["2"]

set.remove(2);
console.log(set.values()); //outputs []

So, now we have a very similar implementation of the Set class as in ECMAScript 6. As mentioned before, we could also have used an array instead of an object to store the elements. As we used arrays in Chapter 2, Arrays, Chapter 3, Stacks, and Chapter 4, Queues, it is nice to know there are different ways of implementing the same thing.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset