In JavaScript collection is a collection, a group of data that can consist of various types of data that have different behaviors. The collection of the data can have various data types or this collection itself can be a specific data structure.
If you have not yet, after this post, I recommend you to read my post about data types in JavaScript and then data structures.
There can be 3 types of collection in Javascript:
- Indexed collections
- Keyed collections
- DOM collections
Indexed collections
The indexed collection contains data that is ordered by index and can also be accessed using this index. An index means the position of data. For example, imagine people in a queue. The person closest to the counter has an index of 0, so they are at position 0. The next person has an index of 1, then the next one is 2, and so on. In JavaScript, the index always starts with 0.
Indexed collection can be an Array or TypedArray. Let's go through each of them.
Array
The most popular and well-known collection of data is an array. An array is an index collection of ordered elements that have their own index and can be accessed by this index. Arrays can contain any data type and even other arrays. Regular arrays are not type-based. This means that you don't have to define what data type you are going to use inside the array. You can use everything together and anytime you want.
Creating arrays
There are several ways to create an array. The easiest and most modern way is to create a variable with a value of empty square brackets. Later on or right away, you can place there any data you need.
Accessing array elements
To excess an array item, you can use an index (position) of the target item. Just don't forget that index starts with 0 so the first element is index 0, not 1.
Destructing arrays
Destructing is a way of extracting data from an array in the most efficient way. Arrays can be very complex at times and even have several levels.
The most simple way to retrieve elements from an array and very easy for beginners to understand is targeting the elements by index. For example, if you need a specific item, you can count its index and retrieve this element.
Looks easy, right? But let's say I want to retrieve more.
But what if I have 100 elements? You are going to spend so much time retrieving everything one by one. By using a destructing we can minimize the code and time.
It looks so much shorter and easier now. As you can see I placed new variable names in an array and each variable matches the value in an array by its position. My first variable is apple and the first element was assigned to an apple. There are many tricks and this is not the only thing you can do with destructuring.
Image that you want to retrieve only the first value and save the rest separately. It is absolutely possible!
I repeated almost the same logic with a little difference. The first element is saved accordingly, just like before. However in order to save the rest of the elements I am using a rest parameter (the three dots) and a desired name of the variable.
What happens if we don't use the rest parameter? It will not save the rest of the elements from the array but save the one according to the position (index) and the rest will be simply ignored. Let's use the exact same example but without the rest parameter.
Another trick that you can use is skipping the specific index. For example, you want to retrieve the first and the third element. To do so, you can write an empty space a comma, and the next variable name.
As you can see, we skipped an orange and moved on to the last element.
Arrays can often be flexible and change a lot. What happens if you add more variables than there are elements in an array? Nothing, it will simply be undefined.
However, it might not be convenient because you don't want the website users to see any values they don't understand. Until they are reading my post haha. To solve this issue you can also add a default values which will replace this undefined until there is an actual value.
Array methods
Arrays have various methods that can perform fast operations on the array without having to write everything from scratch. Methods are something similar to functions (not the same) that already exist in JavaScript so you don't have to create anything yourself. You can read more about the array methods here.
TypedArray
Typed arrays are not arrays. It's an array-like object, specifically a buffer. A buffer in JavaScript is a memory space (usually RAM) that stores binary data. This buffer, a typed array, stores integers in a specific order that is very similar to an array. Compared to arrays, typed arrays cannot be altered once created. Even though arrays and typed arrays are different, they do share several methods but not all of them.
You might ask, if they are so similar why does a typed array exist in the first place? As we already discussed, regular arrays can hold any data which we can manipulate at any time. However, the web keeps developing and there is more usage of heavy data like videos and audio. Such data is much harder to work with for the browser so it needs more time and effort! Of course, this is more complicated than this behind the scenes but all you need to know right now is that typed arrays store raw data that works much faster in the browser hence improving the performance which is vital for heavier and more complicated data.
TypedArray architecture
The typed array is split into two parts - buffers and views. A buffer contains just data (memory) and nothing else while a view turns this data into a typed array by providing a data type of context (a data type that turns data into a typed array), a starting offset, and the number of elements.
Creating TypedArray
In order to use a typed array you need to create an ArrayBuffer and then view it. When creating one you can either create a view first by indicating the desired size and type or you can create an array buffer and then the view that points to it.
The views can be various and a single array buffer can have different views.
Let's create a view for a typed array with a specific size and type.
We created a view with the type Uint8Array and a size of five. We are going to cover the view types very soon so it's fine if you don't understand what it means right now. Let's see what it shows in the console.
Pay attention to zeros. The five zeros you see is the size we have set previously. But they are empty right now and there is no data yet. Time to save some data there by using an index just like in an array.
Let's see the console again. Can you guess what will change?
As you see, there is now the number 10 at the index 0 and 20 at the index 3, just as we set it.
Time to create an array buffer first and then the view, so it's a revered way of a typed array creation.
Looks very similar, right? As you understand, the result is exactly the same.
TypedArray views
Typed array views can be different as they can view different numeric types, for example, Int8, Uint32, Float64, and so on. It all depends on what you are working on and what type of data you need to process.
When to use TypedArray
Typed arrays are often used with web APIs. An API is a middleman between the application and the server. For example, when you are registering somewhere and then logging in, all the information is sent back and forth. By using a website you ask an API what you want from the web server.
Browsers have various built-in APIs that use the typed arrays.
WebGL
WebGL(Web Graphics Library) was the first API where the typed arrays were used. It is an API that renders interactive 3D and 2D graphics without having the need to download or install anything. In our case of JavaScript, we can render it in HTML Canvas.
Canvas 2D
This API also works along with HTML Canvas and uses typed arrays. It provides various methods, objects, and properties to draw and manipulate the canvas element in real-time.
XMLHttpRequest Level 2
The XMLHttpRequest2 API allows JavaScript to make HTTP requests in the browser. Before it, you had to parse a string into a typed array but now you can directly receive a typed array response.
FileReader
FileReader is a method of reading the content of a blob or a file. A blob is a file-like raw data read as binary data or text. A file is a specific kind of blob. This is where typed arrays come into play as we have to work with binary data.
Converting TypedArray
You can convert typed arrays from one type to another by using ArrayBuffer and you can also convert a regular array into a typed array.
To convert an array into a typed array you can use a built-in method typedArray.from() that accepts three parameters typedArray.from(source, map, arg). The source is a target array, the map is an options parameter for a function that is called on each element and the arg is the optional value used in the map function.
Converting from one typed array to another works just as simply. You simply target the desired typed array view and pass the typed array you want to convert.
Keyed collections
A keyed collection is a data collection ordered by keys instead of an index and consists of key-value pairs. In JavaScript, we have two types of keyed collections - Map and Set.
Map
A map is a simple key-value pair object where you can iterate through elements in insertion order. Keys always need to be unique just like an index. To create a map you can use a map constructor and then add the desired elements by using a set method. The key name needs to be a string and a value can be any data type.
In order to access the key, you can use a get method and the key name. If you use a key that doesn't exist, it will return an undefined.
Map methods
Maps have various built-in objects including the set and get which are the most used ones.
Map.prototype.clear()
This method removes all existent key-value pairs and resets the map.
Map.prototype.delete()
The delete method returns true if the key existed and has been removed and false if it doesn't exist.
Map.prototype.entries()
Returns a new iterator object with all key-value pairs. Iterator means that you can iterate over it, and loop over each key-value pair.
Map.prototype.keys()
This method is very similar to the previous one and also returns an iterator object that includes only keys.
Map.prototype.values()
The same works for this method but instead of keys it returns the values of the object.
Map.prototype.forEach()
This method works similarly to the forEach method in arrays. It executes a function for each key-value pair in the map in insertion order.
Map.prototype.has()
The has method checks whether there is a value associated with the key that we are passing into this method. So it returns either true or false. Note that you need to pass the key, not the value.
Iterating over maps
To iterate over maps you can use a for…of loop. To target both key and value, a very easy way to do so is the usage of destructuring.
The same can be achieved using an entries method on the maps object. Compare them and find the difference.
If you want to work only with the keys, you can use another method for keys.
And the similar for working only with the values.
WeakMap
A weak map in JavaScript is also a key-value pair collection. They look very similar however they are not exactly the same. Before we go further, let's create a simple weak map.
Difference between maps and weak maps
In weak maps, the keys can only be objects (or non-registered symbols) while values are more flexible.
However, what is the reason that it's called weak? The reason behind it is the fact that these key objects are a target of so-called garbage collection.
Garbage collection
Garbage collection in JavaScript is automatic memory management. It monitors the memory allowance aka memory allocation and determines whether a specific block of memory is needed or not and gets rid of it if so. When the object keys are collected, their respective values are also collected unless they are strongly referred to something else, an object in this case.
In weak maps, you also don't have methods to access the keys as they are weak. Weak maps hold the reference to the key, not the key itself.
You cannot iterate over the weak map like you can with the map but they do share some similar methods like get, set, delete, and has.
Why do we need weak maps?
One of the main uses of weak maps is a memory leak. A memory leak in JavaScript is a process when JavaScript keeps allowing the specific block of memory and doesn't release it. The unused memory in the system keeps piling up, there is less memory and as a result, an application might crash.
Unused memory can take place in different situations. For example, unused event listeners that are attached to specific elements. An element might be removed but the unused event listener is still there. Now imagine a lot of event listeners like that that use a memory but we don't even need them. Other causes of memory leaks can be callbacks, timers (e.g. setTimeout), closures, and large data structures.
For instance, imagine that you want to track how many times a user clicked the ads on your website.
First, we will create a regular map where the user will be the key and the value will be how many times they clicked the ad. When the user leaves the website, we don't need the information about the clicks anymore.
Once the user object is gone, the object will remain in the map as a key so we manually would need to clear that map because it is not garbage collected and takes up the space in the memory. If you have a bad website maybe hardly anyone visits it but imagine if you had a lot of visitors. To test this, let's log the map to the console and see what it contains.
As you see, even though we said that the object of the user is null ("empty") the map still holds the information about the user.
This is where a weak map comes into play. A weak map is going to "weakly" refer to the user object which will allow the process of garbage collection. We are simply going to replace a map with a weak map and then see what happens to the information about the user once we try to clean it.
As you can see, there is no more user object and a weak map got garbage collected! So awesome, right?!?!
Set
Set is another keyed collection that consists of values. What makes this collection special is the fact that you can have only unique values and it's impossible to add any repetitive data. When you try to add a repetitive value, it will keep the one you added first.
Let's create a simple set to understand how it works.
Next, I am going to try to add the repetitive value apple. What will happen?
Nothing. There is not going to be added anything. The repetitive value is not going to be added and the old one removed. It will be ignored. The apple result you see is the old value, not the new one. Because elements in the set are placed in the insertion order so if it was the new value it would be the last, not the first.
Set methods
Just like maps, sets also have various methods. Compared to maps we use add instead of set to add new values.
Set.prototype.clear()
This method removes all existent values and empties the set. But when you check an empty set it returns undefined. Instead, you can use a size property and it will show 0 when it's empty.
Set.prototype.delete()
As the name suggests the delete method removes the value that we indicate.
Set.prototype.entries()
This method creates an iterable object of key-values where keys are equal to values in this case. So it becomes something similar to a map
Set.prototype.keys()
This method returns an iterable object containing values. Set.prototype.values() is exactly the same.
Set.prototype.has()
Just like in a map, has returns true or false depending on whether it has a value we passed to the method.
Set.prototype.forEach()
This method executes a function for every single value in their insertion order.
Iterating over sets
You can iterate over sets using a for…of loop.
WeakSet
A weak set compared to a regular set is a collection of objects while sets can contain any data type. A weak set, just like a weak map holds information "loosely", for the purpose of temporary storage. Just like a set, a weak set also has unique elements and they are not repetitive. The weak sets just like weak maps are also used for garbage collection so make sure not to skip the topic about garbage collection that I have mentioned earlier.
A weak set also supports methods like add, has, and delete however it cannot be iterated over and doesn't have a size property. A weak set usage is very good for the yes/no situation when let's say you are tracking your users and you check how many people are online right now. But when they log out you don't want to save the objects of the users. Just like with the weak maps, similar logic works with the weak set as it's garbage collected.
DOM collections
If you already had a chance to work with HTML and Vanilla JavaScript together, you most likely have already worked with DOM collections. The skeleton of the website is built on HTML that consists of various tags. For a button there is a button tag, for a header there are various tags depending on the size, and so on. All these tags are objects. When you want some action to be done by clicking on the button, you target the button object with the help of JavaScript. You can target any HTML tag and you can do whatever you want with them.
Sometimes, you might want to target all the buttons on the current page. The collection of these buttons becomes a DOM collection which is an array-like collection. It's more similar to an index collection though it looks like an array.
Let's check how many span tags there are on the main page of Amazon. To achieve that we can target span elements by using document.getElementsByTagName("span"). This will target all the spans.
Wow, so many spans!! And here we got all the spans in just one line.
Conclusion
Congratulations! You reached the end of the JavaScript collections and this is a great start if you are a beginner.
I hope you learned or re-learned something about indexed collections that are based on index values, keyed collections that consist of key-value pairs, and DOM collections that are simply groups of HTML tags. Of course, there is much more to learn about collections however if you are just getting started I believe this is somewhat enough for the start!
Top comments (4)
The concepts of typedArrays, map´s and set´s are a bit confusing, as you can do the same with objects and arrays, just with less limitations. This sounds like a strange language concept to me.
Introducing limitations is done by declaring types in other languages, so if you declare an array of int16, this is a property of the variable holding the array. If you do the same in Javascript, the result will be confusing too:
Beside the introduction of some specific limitations, are there any other advatanges of using maps and sets over conventional objects and arrays in Javascript?
There are some more differences that you can check out in this post and also go through comments what people have to say: dev.to/faisalpathan/why-to-use-map...
Personally, I think that even though there are some small differences, at the end of the day, you can achieve same things with both map/set or objects but sometimes it's just a personal preference or preference of the "company" you work for.
Great thank you
Thank you too!