Modern Javascript Recap

Joris Verbogt
Joris Verbogt
Jan 14 2022
Posted in Engineering & Technology

Using smarter data types

Modern Javascript Recap

Since the introduction of modern JavaScript with ES5 and later ES6, common language patterns and structures were introduced that lifted the limited expressive power of the original JavaScript language to the level of more robust programming languages.

This ranges from syntactic sugar such as classes, async/await and arrow functions, to new language features such as variable scope, generators and destructuring.

In this blog post, I would like to focus on two important new standard types in Javascript: Map and Set. Although these data types have existed in some form or another in many languages, in Javascript they were always implemented using some form of ad-hoc Objects and Arrays. When you are maintaining a large code base which was developed over a longer period of time, you may still come across code that doesn't use these modern data types, but could heavily benefit from it.

For those of you who are wondering if you can use it in your project: these types have been part of ES6 since 2015, so they are supported in all the modern browsers (and NodeJS). If you really need to support older browsers, there's always the Babel Polyfill

Map

First of all, Map. Generically speaking, a Map is a collection of data tuples (pairs) with a unique key and a value mapped to that key. A Map can be used to quickly look up data by its key.

Comparison with Object

Traditionally, this was achieved in JS with a plain object. There are, however, a couple of very important differences:

  • keys in Map can be of any type, in an Object they can only be simple types like String, Number and Symbol
  • keys in Map are only the ones you put in. In an Object, the prototype of the Object already adds some default keys, which may collide with your keys.
  • keys in Map are ordered by insertion. In an Object, there is no guaranteed order (at least not before ES2015)
  • a Map has a size property, an Object needs manual calculation of its size
  • a Map is iterable, an Object has limitations on how to iterate (there is of course for..in but only for enumerable properties)
  • a Map performs better if the data set is dynamic, i.e., lots of additions and removals. Object is not well suited for those scenarios.

Examples

Create

let obj = { id: 1, name: 'Test'}

let map = new Map(['id', 1], ['name', 'Test'])

Check

obj.id !== undefined // true

map.has('id') // true

Add

obj.language = 'en'

map.set('language', 'en')

Remove

delete obj.id

map.delete('id')

There is also Map.clear() which has no equivalent in Object. You would need to iterate over all properties.

Iterating

for (let key in obj) {
  console.log(`${key}: ${obj[key]}`)
}

map.forEach((value, key) => {
  console.log(`${key}: ${value}`)
})

When to use

You could say, in general, an Object is suited for struct-like data types with fixed properties. They are easily serializable to JSON to be used in data exchange between systems.

A Map should be used whenever operations and transformations need to be performed on dynamic data sets, as it performs better in those use cases.

Set

Generically speaking, a Set is a collection of distinct data values.

Without a Set type, you would need to use an Array and check if a value was in it before adding it:

let arr = ['dog', 'cat', 'mouse']

function add(arr, value) {
  if (arr.indexOf(value) === -1) {
    arr.push(value)
  }
}

Even worse with removing:

function remove(arr, value) {
  let index = arr.indexOf(value)
  if (index >= 0) {
    arr.splice(index, 1)
  }
}

All very error-prone!

With Set, this is all much more straight forward:

let set = new Set(['dog', 'cat'])

set.has('cat') // true
set.add('mouse') 
set.delete('cat')

Converting from Set to Array is also easy:

Array.from(set) // ['dog', 'mouse']
  
[...set] // ['dog', 'mouse']

And we can now comfortably create set operations:

function union(setA, setB) {
  let _union = new Set(setA)
  for (let value of setB) {
    _union.add(value)
  }
  return _union
}

function intersect(setA, setB) {
  let _intersect = new Set()
  for (let value of setA) {
    if (setB.has(value)) {
      _intersect.add(value)
    }
  }
  return _intersect
}

When to use

In general, whenever you need to be sure there is an actual set of data values with no duplicates, use Set. In other cases, especially if you need an explicit order, keep using the good ole' Array.

Conclusion

Although these data types have been around for years in Javascript, chances are your existing code is still not using these powerful data types. If not: give them a try!

As always, we hope you liked this article and if you have anything to add, maybe you are suited for a Developer position in Notificare. We are currently looking for a Core API Developer, check out the job description. If modern Javascript is your thing, don't hesitate to apply!

Keep up-to-date with the latest news