Modern JavaScript Part 16: Sets, WeakSets, Maps and WeakMaps

Sun Jun 03 2018 / book

What is a Set?

A Set is an object where we can store unique values of any type.

    // create our set
    const family = new Set();

    // add values to it
    family.add("Dad");
    console.log(family);
    // Set [ "Dad" ]

    family.add("Mom");
    console.log(family);
    // Set [ "Dad", "Mom" ]

    family.add("Son");
    console.log(family);
    // Set [ "Dad", "Mom", "Son" ]

    family.add("Dad");
    console.log(family);
    // Set [ "Dad", "Mom", "Son" ]

As you can see, at the end we tried to add "Dad" again,but the Set still remained the same because a Set can only take unique values. Let's continue using the same Set and see what method we can use on it.

    family.size;
    // 3
    family.keys();
    // SetIterator {"Dad", "Mom", "Son"}
    family.entries();
    // SetIterator {"Dad", "Mom", "Son"}
    family.values();
    // SetIterator {"Dad", "Mom", "Son"}
    family.delete("Dad");
    // true
    family;
    // Set [ "Mom", "Son" ]
    family.clear;
    family;
    // Set []

As you can see a Set has a size property and we can delete an item from it or use clear to delete all the items from it. We can also notice that a Set does not have keys so when we call .keys() we get the same as calling .values() or .entries().  

 

Loop over a Set

We have two ways of iterating over a Set: using .next() or using a for of loop.

    // using `.next()`
    const iterator = family.values();
    iterator.next();
    // Object { value: "Dad", done: false }
    iterator.next();
    // Object { value: "Mom", done: false }

    // using a `for of` loop
    for(const person of family) {
      console.log(person);
    }
    // Dad
    // Mom
    // Son

 

Remove duplicates from an array

We can use a Set to remove duplicates from an Array since we know it can only hold unique values.

    const myArray = ["dad", "mom", "son", "dad", "mom", "daughter"];

    const set = new Set(myArray);
    console.log(set);
    // Set [ "dad", "mom", "son", "daughter" ]
    // transform the `Set` into an Array
    const uniqueArray = Array.from(set);
    console.log(uniqueArray);
    // Array [ "dad", "mom", "son", "daughter" ]

    // write the same but in a single line
    const uniqueArray = Array.from(new Set(myArray));
    // Array [ "dad", "mom", "son", "daughter" ]

As you can see the new array only contains the unique values from the original array.  

 

What is a WeakSet?

A WeakSet is similar to a Set but it can only contain Objects.

    let dad = {name: "Daddy", age: 50};
    let mom = {name: "Mummy", age: 45};

    const family = new WeakSet([dad,mom]);

    for(const person of family){
      console.log(person);
    }
    // TypeError: family is not iterable

We created our new WeakSet but when we tried to use a for of loop it did not work, we can't iterate over a WeakSet. Another big difference that we can see is by trying to use .clear on a WeakSet: nothing will happen because a WeakSet cleans itself up after we delete an element from it.

    dad = null;
    family;
    // WeakSet [ {…}, {…} ]

    // wait a few seconds
    family;
    // WeakSet [ {…} ]

As you can see after a few seconds dad was removed and garbage collected. That happened because the reference to it was lost when we set the value to null.  

 

What is a Map?

A Map is similar to a Set, but they have key and value pairs.

    const family = new Map();

    family.set("Dad", 40);
    family.set("Mom", 50);
    family.set("Son", 20);

    family;
    // Map { Dad → 40, Mom → 50, Son → 20 }
    family.size;
    // 3

    family.forEach((key,val) => console.log(val,key));
    // Dad 40
    // Mom 50
    // Son 20

    for(const [key,val] of family){
      console.log(key,val);
    }
    // Dad 40
    // Mom 50
    // Son 20

If you remember, we could iterate over a Set only with a for of loop, while we can iterate over a Map with both a for of and a forEach loop.  

 

What is a WeakMap?

A WeakMap is a collection of key/value pairs and similarly to a WeakSet, even in a WeakMap the keys are weakly referenced, which means that when the reference is lost, the value will be removed from the WeakMap and garbage collected. A WeakMap is not enumerable therefore we cannot loop over it.

    let dad = { name: "Daddy" };
    let mom = { name: "Mommy" };

    const myMap = new Map();
    const myWeakMap = new WeakMap();

    myMap.set(dad);
    myWeakMap.set(mom);

    dad = null;
    mom = null;

    myMap;
    // Map(1) {{…}}
    myWeakMap;
    // WeakMap {}

As you can see mom was garbage collected after we set the its value to null whilst dad still remains inside our Map.