Comments

JavaScript: Objects, Arrays, Maps and Sets

Not so long ago, JavaScript used to have only two ways of representing complex data: objects and arrays (OK, technically, an array is an object, but for the sake of simplicity, let’s make a distinction between them in this article). Now, this scenario has changed and we have a number of new data structures to use. In this article, I’ll talk about two of them: Maps and Sets. Let’s see when to use them and how they compare to objects and arrays. Just keep reading.

Objects

Objects are the base of every non-primitive types in JavaScript. Except for booleans, numbers, strings, symbols, null and undefined, everything else is an object, including arrays, regexes and even maps and sets. To make things simpler, in this article we will apply the term “object” solely to refer to “plain” objects, created by using the literal syntax.

Here’s an object representing some data:

const person = { 
  name: 'John Doe', 
  city: 'San Francisco', 
  age: 28,
};

An object is basically a collection of key-value pairs. You can set the value to anything, including other objects. However, an object’s key can only be a string or a symbol. You may try to use other types of value (like a number or an object) as key, but the JS engine will simply convert it to string before storing it into the memory.

For example:

const someNumber = 321;
const someObject = { age: 42 };
const anotherObject = {
  [someNumber]: 'value one',
  [someObject]: 'value two',
};

Likewise, when you try to access some property of an object by using a key that is not a string nor a symbol, JS will implicitly convert it to string as well. For numbers, this conversion will work just fine, since the output will always be unique (e.g.: 1234 will be converted to ‘1234’). Here’s an example of how to access a property of the object we’ve declared in the above snippet:

console.log(anotherObject[321]); //=> value one
console.log(anotherObject['321']); //=> value one

But how about the property in which we used an object key? Well, if you try to access that property by using someObject as key, it will work:

console.log(anotherObject[someObject]); //=> value two

Now, try to do this:

console.log(anotherObject[{}]); //=> value two

As you can see, even using a totally different object as key, the JS engine retrieves the same property originally associated to someObject. That happens because, after the implicit conversion that I’ve mentioned, the string representation of any plain object will always be the same: “[object Object]”:

console.log({}.toString()); //=> "[object Object]"
console.log({ name: 'John Doe' }.toString()); //=> "[object Object]"

Therefore, you could’ve accessed that property simply by doing this:

console.log(anotherObject['[object Object]']); //=> value two

Pros:

  • It’s really easy to declare an object by using the literal syntax.
  • Supported by every browser, without polyfills.

Cons:

  • It only accepts strings and symbols as keys.
  • Objects are not enumeration-friendly. A given instance doesn’t provide its own iteration methods. You need to use external methods like Object.keys, Object.values or Object.entries to iterate over an object’s properties (or even use a for…in loop).
  • Even though as of ES2015 objects started to preserve the order in which their properties were inserted, things are not so simple.

Map

Maps also allow us to create a collection of key-value pairs. However, they provide some important improvements:

const objKey1 = {};
const objKey2 = {};
const numberKey = 321;

const map = new Map();
map.set(objKey1, 'this is the value of the first item');
map.set(objKey2, 'this is the value of the second item');
map.set(numberKey, 'this is the value of the third item');
map.set('someStringKey', 'this is the value of the fourth item');

// retrieving the values of a map is quite easy 
console.log(map.get(objKey1)); //=> 'this is the value of the first item'
console.log(map.get(objKey2)); //=> 'this is the value of the second item'
console.log(map.get(numberKey)); //=> 'this is the value of the third item'
console.log(map.get('someStringKey')); //=> 'this is the value of the fourth item'

Differently from objects, JavaScript has no special syntax for maps. To create a map instance, you need to use the Map constructor. Similarly, all operations that can be performed on a map are available as methods, such as set, get, has and forEach.

As you may have noticed, maps allow us to use any type of value as key. Basically, by using them, you can associate any two data types you want.

Maps are also iteration-friendly:

map.forEach((value, key) => console.log('key:', key, ', value:', value));
//=> key: {} , value: this is the value of the first item
//=> key: {} , value: this is the value of the second item
//=> key: 321 , value: this is the value of the third item
//=> key: someStringKey , value: this is the value of the fourth item

Pros:

  • Maps are easy to iterate over. In order to do so, a map instance provides methods like forEach(), entries(), keys() and values().
  • Maps allow you to use virtually anything as a key: strings, numbers, symbols, objects, booleans and so on and so forth.
  • You can easily check things like the length of a map (by using the map.size instance property) or if it contains a specific value (with the map.has() method).

Cons:

  • There’s no special syntax for maps. Everything is purely OOPish when it comes to declare or manipulate maps (I mean, using a constructor explicitly for creating them and methods for doing anything).

Array

You’re probably familiar with arrays, but it  won’t hurt to see a brief definition, right? Well, in simple words, arrays are collections whose values are indexed by positive numbers, starting from zero. They’re stored sequentially in the memory and you can access a specific item directly, by using its index:

const colors = ['red', 'blue', 'black', 'green', 'white'];
console.log(colors[0]); //=> "red"
console.log(colors[2]); //=> "black"

Arrays also allow you to store repeated values in a same instance:

const colors = ['red', 'blue', 'red', 'blue'];
console.log(colors); //=> ['red', 'blue', 'red', 'blue']

Pros:

  • Similarly to plain objects, JavaScript also has a literal syntax for arrays.
  • Supported by every browser, without polyfills.
  • Arrays allow you to set or read a value on a specific index directly.
  • Accessing the items of an array is really fast, since they’re stored consecutively in the memory.

Cons:

  • By default, arrays won’t ensure that items are unique in a given instance. To do so, you need to filter values manually.

Set

Sets are not meant to be a replacement for arrays. In fact, these two types of collections have a number of significant differences. For instance, sets don’t allow repeated values:

const colors = new Set(['red', 'green', 'blue', 'red', 'blue']);
console.log(colors); //=> Set(3) {"red", "green", "blue"}

As you can see, we tried to instantiate a set with repeated values, but this is simply not possible. Sets ensure uniqueness of values.

Sets also won’t allow you to access an item directly. If you know which value you’re looking for, you can check if a set contains it, by using the .has() method, or you can even delete this value easily, by using delete(). But if you want to find, let’s say, the third item of a set (following the insertion order), you have only two options:

  1. Iterating over it, with the forEach method or by using the iterators that are returned by the .keys()/.values() methods.
  2. Converting the set to an array and handling it however you want. Please note that, under the hood, the JS engine iterates over the map anyway in order to produce the output array.

Pros:

  • It’s easy to ensure uniqueness of values.

Cons:

  • No literal syntax available.
  • Since items are not indexed, you can’t access them directly. To access an item by its “position” (I mean, the order in which it was inserted), you need to iterate over the set.
  • Iterating through a set consumes more CPU than iterating over an array.

Conclusion

  • Plain objects are the right choice when string (or symbol) keys are enough for you.
  • Maps are the way to go if you want to create an association between any two data types (like two objects, for example).
  • Arrays are the ideal option when direct access to items is crucial and uniqueness of values is not required.
  • Sets are useful when you want to ensure that values don’t repeat in a same instance.

Do you want to become a JavaScript master? If so, you need to check out Mosh’s JavaScript course. It’s the best JavaScript course out there! And if you liked this article, share it with others as well!

JavaScript hacker, front-end engineer and F/OSS lover.
Tags: , , , ,

Leave a Reply

Connect with Me
  • Categories
  • Popular Posts