Advanced Javascript Collections: Introduction to Sets and Maps

honeybadger_staff

Honeybadger Staff

Posted on June 28, 2023

Advanced Javascript Collections: Introduction to Sets and Maps

This article was originally written by Adebayo Adams on the Honeybadger Developer Blog.

It's safe to say that arrays and objects are JavaScript's most useful, popular, and overused collections. However, there are cases where neither of them solves your problem effectively, or their limitations are getting in the way, thus making your solution more complex than it should be.

In this article, I'll discuss everything you need to know to leverage sets, maps, and their unique features to solve complex data structure problems.

Prerequisites

To follow this tutorial, you need to understand JavaScript. It'll help if you also know one or two limitations of arrays and objects.

In the next section, we’ll explore sets and how to use them.

Sets

A set is a type of JavaScript collection that can store all supported types in JavaScript, including object references.

A set is unordered, cannot contain duplicate values, and has rapid access to the data it contains.

There are two common ways to create a set in JavaScript. The first is passing an array to the constructor:

const ids = new Set(['ADM78921', 'ADM78921', 'ADM73212', 'ADM70213']);
console.log(ids);
Enter fullscreen mode Exit fullscreen mode

set without duplicate

The image above shows the set in the console; notice that the duplicate value was stripped out of the result.

The second common way to create an empty set and use the chainable add method to add values:

const ids = new Set()
.add('ADM78921')
.add('ADM73212')
.add('ADM70213')
.add('ADM78921')
console.log(ids);
Enter fullscreen mode Exit fullscreen mode

The code above will return the same result as the previous image.

As mentioned earlier, a set can contain any value. Here is an example:

const hobbies = ["singing", "dancing", "horse riding"]
const diffValSet = new Set()
.add(5)
.add('5')
.add({name: "Adams", age: 22, occupation: "Software Developer"})
.add(hobbies)

console.log(diffValSet)
Enter fullscreen mode Exit fullscreen mode

The code above creates a set named diffValSet and will return the result:

multiple types set

We’ll look at some of the most common set methods in the next section.

Set Methods

The set collection comes with very helpful methods that make working with the collection easy. Let's look at some of the methods.

Adding New Values

You can use the add method to add new values to the set:

ids.add('ADM75209');
ids.add('ADM75209');
console.log(ids)
Enter fullscreen mode Exit fullscreen mode

The code above adds a new string value to the set and returns the following result:

add method example result

Note: The value ADM75209 was only added once, even though I called the add method with the same value twice.

Let's see how to count the values in a set next.

Counting Values

Sometimes, you might want to limit your set to a specific number of values.

You can get the number of values in your set using the size property:

console.log(diffValSet.size) // returns 4
Enter fullscreen mode Exit fullscreen mode

We’ll look at how to loop through sets in the next section.

Looping Through Sets

Iterating values is almost unavoidable when developing apps and websites. You can loop through sets by using the forEach method:

const winnersIds = new Set(['ADM78921', 'ADM73212', 'ADM70213', 'ADM75209', 'ADM75119'])

winnersIds.forEach((value)=>{
    console.log(`${value} won`)
})
Enter fullscreen mode Exit fullscreen mode

The code above creates a winnersIds set and then loops through it using the forEach method. The code above should produce the following result:

loop result

Notice how the values came out the same way they were inserted upon creation.

We’ll look at how to delete values from sets next.

Deleting Values

You can delete an existing value from a set by calling the delete method on the set:

diffValSet.delete(5)
console.log(diffValSet)
Enter fullscreen mode Exit fullscreen mode

The code above will delete the number 5 from the diffValSet set:

delete value result

Note: You can only delete one value simultaneously using the delete method. That means trying to delete multiple values like diffValSet.delete(5, hobbies) would not work.

However, deleting objects like the one inside diffValSet is not as straightforward, so you need to use a loop hack with the forEach method:

 diffValSet.forEach(item => { if (item.age === 22) diffValSet.delete(item); });
Enter fullscreen mode Exit fullscreen mode

The code above will loop through the set, check for any key inside the object, and delete the whole object if the condition is true.

Next, let's look at how to check whether a value exists in a set.

Checking values

It is considered good practice to check whether a value exists in your code before deleting or adding a new value.

To check if the value exists in a set, you can use the has method:

console.log(diffValSet.has(hobbies)) // returns `true`
Enter fullscreen mode Exit fullscreen mode

The code above will return true because hobbies exists in the diffValSet set.

Let's see how to delete all the values in a set.

Clearing a Set

Sometimes, you might want to delete all the values in the set at once.

You can do that with the clear method:

diffValSet.clear()
console.log(diffValSet)
Enter fullscreen mode Exit fullscreen mode

clear set result

The image above shows the size of the set as 0 after running the clear method on it.

Next, let's see the difference between sets and arrays.

Differences between Sets and Arrays

In the previous sections, you learned what sets are, how to use them, and the common set methods.

This section will show you the common differences between sets and arrays.

Unique values

As you have learned in the previous section, sets can only contain unique values, unlike arrays, which can contain duplicate values. You can remove duplicates from arrays by putting them inside a set:

const topWebsites = ['google.com', 'facebook.com', 'twitter.com', 'youtube.com', 'google.com', 'quora.com']

const uniqueSites = new Set(topWebsites)

console.log(uniqueSites)
Enter fullscreen mode Exit fullscreen mode

The code above declares a topWebsites that has duplicate values and converts it to a uniqueSites set that strips out the duplicate google.com values from the topWebsites:

array conversion

The image above shows a set that contains unique values.

You can convert the uniqueSites set back to an array using the spread operator:

console.log([...uniqueSites])
Enter fullscreen mode Exit fullscreen mode

The code above will return the uniqueSites as an array:

set to array conversion

Accessing Values

When using arrays, you can access values inside the array using the index:

const proteins = ['beef', 'pork', 'chicken', 'egg', 'bacon']

console.log(arr[2]) // returns 'chicken'
Enter fullscreen mode Exit fullscreen mode

The code above declares a proteins array and logs the item's value in the second index to the console.

What I just demonstrated above is impossible when using sets because the index is the same as the value, so you need to know the value to retrieve it from the set.

You can use the has method to check this.

Order of Insertion and Deletion

Unlike arrays, where you can insert and delete items in a specific index, sets only add new items to the end of the set using the add method and deletes an item by referencing the value using the delete keyword, as you saw earlier.

Those are the significant differences between sets and arrays in JavaScript.

Next, let's look at WeakSets and how to use them.

WeakSets

A WeakSet is a collection type that is similar to Sets, but it can only contain objects and is automatically garbage-collected. The most notable difference between Sets and WeakSets is that WeakSets only has three methods, called add, has, and delete, and they work the same way they do on sets.

Let's look at how to create a WeakSet next.

Creating a WeakSet

The easiest way to create a WeakSet is by using the new keyword:

const winners = new WeakSet([{1: '@logan'}, {2: '@mekako'}, {3: '@oddone'}])
Enter fullscreen mode Exit fullscreen mode

The code above declares a WeakSet that contains three objects:

weakset

Another way to declare a WeakSet is to put object references inside the square brackets:

const person = {name:"Jane", age: 18}
const school = {name: "Griffiths High School", town: "Maryland"}
const home = {streetNo: "12b", streetName: "Westend", town: "Osapa London"}

const userDets = new WeakSet([person, school, home])

console.log(userDets)
Enter fullscreen mode Exit fullscreen mode

The code above declares three different objects and then creates a WeakSet that includes the three object references:

weakset references

The significant differences between sets and weaksets are automatic garbage collection and the number of methods.

We’ll see what Maps are in the next section.

Maps

A map is a JavaScript collection that allows you to store key-value pairs, where the keys can be any value, unlike objects, where the keys can only be strings, integers, and symbols. Keys in a map are also unique and can only appear once in the collection.

Creating Maps

The easiest way to create a map collection is to use the new keyword with an array of values:

const num = {
    num: 1
}

const refs = new Map([
    [{num: 1}, "one"],
    [[2], "two"],
    ["iii", "three"]
]); 
console.log(refs);
Enter fullscreen mode Exit fullscreen mode

The code above creates a refs map that contains three values and then logs it to the console. The code above will return the following result:

a map collection

The result is an object with three values with different values as the key: an object, an array, and a string.

If you add another value with an existing key, JavaScript will change the value of the previous key, and the object will remain in the same position. Add the following line to the previous map:

const refs = new Map([
// same as previous
    [2, "three"],
]);
console.log(refs);
Enter fullscreen mode Exit fullscreen mode

The code above adds a value that has the existing 2 key to the previous map, and the result should look like this:

duplicate values

Notice that the image above shows the 2 key in the same position but with a different value of "three".

We’ll look at the common map methods in the next section.

Map Methods

Like set, the map collection also has some handy methods that help developers quickly get things done with them. In this section, I'll show you the different methods you can use on the map collection.

The set Method

Most times, you want to add values to the map dynamically, you can do that using the chainable set method:

const refs = new Map()

refs.set({num: 1}, "one").set(2, "two").set("iii", "three")

console.log(refs)
Enter fullscreen mode Exit fullscreen mode

The code above creates a refs map collection and uses the set method to add values to it. The result should look like this:

set method result

Let's see how to get a single value out of a map.

The get Method

To get the value of a particular key inside the map, you can use the get method:

console.log(refs.get("iii"))
Enter fullscreen mode Exit fullscreen mode

The code above should return "three", which is the value of the iii key inside the map.

Note: You cannot get multiple values from a map by adding multiple parameters to the get method. For example, console.log(refs.get("iii", 2)); will not work and will only return "three" instead of "three" and "two"

The has Method

As mentioned previously, checking whether a value already exists in a collection is considered good practice before adding or deleting it. The has method allows you to check if a map contains the given value:

console.log(refs.has(2)) // returns true
Enter fullscreen mode Exit fullscreen mode

The code above will return true because the map contains the value 2.

Next, let's see how to loop through maps.

The forEach Method

You can loop through maps using the forEach method:

const que = new Map()

que.set(223, "John Sanders").set(421, "Rebecca Stans").set(908, "Wanjiku Kenyamo")

que.forEach((value, key)=>{
    console.log(`${key}: ${value}`)
})
Enter fullscreen mode Exit fullscreen mode

The code above declares a que map with three key-value pairs and then uses the forEach to loop through the map and log the keys and values to the console.

The forEach method gives you access to the values and keys inside the map. The code above should return the following:

map loop result

Next, let's see how to delete a value from a map.

The delete Method

To dynamically delete a value from a map, you must use the delete method:

console.log(que.delete(223)) // returns true
console.log(que.delete(23)) // returns false
Enter fullscreen mode Exit fullscreen mode

The code above deletes the 223 value and returns true upon deletion and false if the value doesn't exist in the map:

delete result

In the next section, let's look at how to clear all the values inside a map.

The clear Method

Maps in JavaScript come with a clear method that allows you to delete all the values in a map in one go:

console.log(que.size) // returns number of values
que.clear() // deletes all the values
console.log(que) // logs the now empty map
Enter fullscreen mode Exit fullscreen mode

The code above checks the size of the que map, deletes all the values in the map using the clear method, and then logs the now-empty map to the console:

clear map result

Next, let's see how to get all the entries, keys, and values in the map.

The entries, keys, and values Methods

The map collection in JavaScript comes with the keys and value methods, and both return an iterable object that contains a MapIterator:

console.log(que.values())
console.log(que.keys())
Enter fullscreen mode Exit fullscreen mode

The code above will log all the values and keys in the map to the console:

values and keys result

The entries method works a bit differently because it returns the keys and values in one iterable object:

console.log(que.entries())
Enter fullscreen mode Exit fullscreen mode

The code above will log both the values and keys in the map to the console in one go:

entries method result

Converting entries, keys, and values to Arrays

Sometimes, you might need just the keys, values, or even entries of a map as an array because they return iterable objects by default.

You can use the spread operator to convert them to arrays:

console.log([...que.entries()]) // returns a two-dimensional array
console.log([...que.keys()]) // returns an array of the keys
console.log([...que.values()]) // returns an array of the values
Enter fullscreen mode Exit fullscreen mode

The code above converts and logs the keys, values, and entries of the map to the console:

array conversion result

The image above shows the keys, values, and entries as arrays in the console; the entries is converted into three arrays containing each item's key and value.

Now that you know how to use a map and its methods let's see the differences between maps and objects in JavaScript.

Differences Between Maps and Objects

This section will teach you about the significant differences between maps and objects.

Key Types

As you learned in the previous sections, you can use any type in JavaScript as a key in maps, unlike objects that only allow strings, integers, and symbols.

Order of Insertion

Items in maps maintain their order of insertion; when performing operations like iterations, the items inside maps come out in the order they were inserted, but this is not the case with objects.

Iterating Values

As you saw earlier, you can use the forEach loop function directly on maps; this is not possible with objects, as they are not iterable by default and can only be iterated using the for...in loop.

These are the significant differences between maps and objects, but there are other differences in how maps and objects are used, such as adding items, checking items, deleting items, and more.

We’ll look at WeakMaps in the next section.

WeakMaps

A WeakMap is very similar to Map, with the only significant difference being that a WeakMap can only contain objects as keys and supports automatic garbage collection.

A WeakMap also has the set, has, get, and delete methods that work the same as they do on a Map.

Creating WeakMaps

You can create a WeakMap using the new keyword:

const type = {
    name: "car",
}

const carWM = new WeakMap()

// the set method
carWM.set(type, "Honda");

console.log(carWM)
Enter fullscreen mode Exit fullscreen mode

The code above creates a type object, creates an empty WeakMap called carWM, and uses the set method to add a "Honda" value with the type object as the key. Then, it logs the carWM to the console. The result should look like this:

create weakmap result

WeakMaps also support the get, has, and delete methods the same way Maps do.

Conclusion

In this article, you learned about Sets, WeakSets, Maps, and WeakMaps, their methods, and how to use them in JavaScript. You also learned the differences between Sets and Arrays, as well as the differences between Maps and Objects.

I hope you now understand how these collection types work in JavaScript, and I also hope this article will help you make informed decisions when deciding when to use them instead of arrays and objects.

That was a long ride! Thank you so much for reading!

💖 💪 🙅 🚩
honeybadger_staff
Honeybadger Staff

Posted on June 28, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related