Blog

Sets and Maps from Four Semesters of Computer Science in 5 Hours by Brian Holt
Posted on August 24, 2020 in Algorithms, JavaScript by Matt Jennings

Set

Set objects are collections of values. Sets are reference data types, like Arrays, and allow unique lists (ONLY unique values unliked Arrays). Sets are also iterable like Arrays.

Sets can’t be accessed like Arrays (like arr[0] = 0;) and don’t have indices.

Example:

let myArray = [11, 22, 34, 65, 34];
let mySet = new Set(myArray);

console.log(mySet)
// Set {11, 22, 34, 65}

mySet.add('100');

console.log(mySet);
// Set {11, 22, 34, 65, '100'}

mySet.delete(22);

console.log(mySet);
// Set {11, 34, 65, '100'}

console.log(mySet.size);
// 4

// Loop through a set using forEach
mySet.forEach(val => {
   console.log(val);
});

// Loop through a set using for of
for(let val of mySet) {
   console.log(val);
}

console.log(mySet.has(11));
// true

// To create empty Set
mySet.clear();

WeakSet

The WeakSet objects are collections of objects, just like Set objects. Also, all objects in WeakSet objects must be unique. However, unlike Set objects, WeakSet objects:

  1. Are collections of objects only. They can’t contain values of any type, like Set objects can.
  2. Are weak, meaning that references to objects in a WeakSet are held weakly. If no other references to an object stored in a WeakSet exist, those objects can be garbage collected, which means they’ll be removed from memory.

Finally, WeakSet objects are not enumerable.

let carWeakMap = new WeakMap();

let car1 = {
   make: 'Hondo',
   model: 'Civic'
};

carWeakSet.add(car1);

console.log(carWeakSet);
// WeakSet {Object: {make: "Honda", model: "Civic"}}

carWeakSet.delete(car1);
// WeakSet {}

Map

The Map object holds key-value pairs and remembers the original insertion order of the keys. Objects and primitive values may be used as either a key or a value.

Some differences between Object and Map objects are:

  1. The keys of Map can be any value, including functions, objects, or primitives.
  2. The keys in Map are ordered. Thus, when iterating over it, a Map object returns keys in order of insertion.
  3. The number of items in a Map is easily retrieved from its size property.
  4. A Map is iterable.
  5. A Map can perform better than an Object in same scenarios involving frequent additions and removals of key-value pairs.

Code examples:

let myMap = new Map()

let keyString = 'a string'
let keyObj    = {}
let keyFunc   = function() {}

// setting the values
myMap.set(keyString, "value associated with 'a string'")
myMap.set(keyObj, 'value associated with keyObj')
myMap.set(keyFunc, 'value associated with keyFunc')

myMap.size              // 3

// getting the values
myMap.get(keyString)    // "value associated with 'a string'"
myMap.get(keyObj)       // "value associated with keyObj"
myMap.get(keyFunc)      // "value associated with keyFunc"

myMap.get('a string')    // "value associated with 'a string'"
                         // because keyString === 'a string'
myMap.get({})            // undefined, because keyObj !== {}


let myMap = new Map()
myMap.set(0, 'zero')
myMap.set(1, 'one')

for (let [key, value] of myMap) {
  console.log(key + ' = ' + value)
}
// 0 = zero
// 1 = one

for (let key of myMap.keys()) {
  console.log(key)
}
// 0
// 1

for (let value of myMap.values()) {
  console.log(value)
}
// zero
// one

for (let [key, value] of myMap.entries()) {
  console.log(key + ' = ' + value)
}
// 0 = zero
// 1 = one
myMap.get(function() {}) // undefined, because keyFunc !== function () {}

let myMap = new Map()
myMap.set(0, 'zero')
myMap.set(1, 'one')

for (let [key, value] of myMap) {
  console.log(key + ' = ' + value)
}
// 0 = zero
// 1 = one

for (let key of myMap.keys()) {
  console.log(key)
}
// 0
// 1

for (let value of myMap.values()) {
  console.log(value)
}
// zero
// one

for (let [key, value] of myMap.entries()) {
  console.log(key + ' = ' + value)
}
// 0 = zero
// 1 = one


myMap.forEach(function(value, key) {
  console.log(key + ' = ' + value)
})
// 0 = zero
// 1 = one

WeakMap

The WeakMap object is a collection of key/value pairs in which the keys are weakly referenced. The keys must be objects and the values can be arbitrary values.

Keys of WeakMaps are of the type Object only. Primitive data types as keys are not allowed (e.g. a Symbol can’t be a WeakMap key).

Native WeakMaps hold “weak” references to key objects, which means that they do not prevent garbage collection in case there would be no other reference to the key object. 

Because the references are weak, WeakMap keys are not enumerable.

Code examples:

const wm1 = new WeakMap(),
      wm2 = new WeakMap(),
      wm3 = new WeakMap();
const o1 = {},
      o2 = function() {},
      o3 = window;

wm1.set(o1, 37);
wm1.set(o2, 'azerty');
wm2.set(o1, o2); // a value can be anything, including an object or a function
wm2.set(o3, undefined);
wm2.set(wm1, wm2); // keys and values can be any objects. Even WeakMaps!

wm1.get(o2); // "azerty"
wm2.get(o2); // undefined, because there is no key for o2 on wm2
wm2.get(o3); // undefined, because that is the set value

wm1.has(o2); // true
wm2.has(o2); // false
wm2.has(o3); // true (even if the value itself is 'undefined')

wm3.set(o1, 37);
wm3.get(o1); // 37

wm1.has(o1); // true
wm1.delete(o1);
wm1.has(o1); // false

Leave a Reply