Javascript

Immutable Data with Immutable.js

June 21st, 2016 | By Jscrambler | 6 min read

In this post, we will explore concepts about working with immutable data and immutable data structures using Immutable.js, which provides us with many highly efficient Immutable data structures.

Immutability refers to how data behaves after being instanced so that no mutations are allowed. In practice, mutations can be split into two groups: visible mutations and invisible mutations.

Visible mutations

Visible mutations are those that either modify the data or the data structure that contains it in a way that can be noted by outside observers through the API.

Invisible mutations

Invisible mutations are changes that cannot be noted through the API. In a sense, invisible mutations can be considered side effects.

The benefits

There are some benefits when compilers and runtimes can be sure that data cannot change:

  • Persistence becomes easier;

  • Copying becomes constant because you can’t change the data. You can only create a new reference from the existing instance of the original data by copying operation;

  • No locks are needed to synchronize data in multiple threads because the data cannot change.


The Immutable.js is a library created by Facebook to work with immutable collections in JavaScript. It provides many persistent immutable data structures like List, Stack, Map, OrderedMap, Set, OrderedSet, and Record.

Setting up the Immutable.js library

First, install the Immutable.js library. This library works on both Node.js and the browser. We use it on Node.js, but all the examples will work the same on the browser.

Let’s install immutable.js on our machine by running this command in the terminal:

npm install immutable


To test our first code, let’s create a simple immutable map:

var Immutable = require('immutable');
var client = Immutable.Map({
    name: 'John',
    age: 25
});
console.log(client.get('name')); // 'John'
console.log(client.get('age')); // 25


After you create a Map, you can use the map.get(‘key’) to access their data by providing a key.

If you need to update some data from this Map, use the map.set(‘key’) simple as that, but this function won’t change the current internal state of this Map because of the immutable behavior of this data structure, so instead of changing it internally, this function will return a copy of the current Map with some data changed.

See the example below:

var Immutable = require('immutable');
var client = Immutable.Map({ name: 'John', age: 25 });
var newClient = client.set('name', 'Mary');
console.log(newClient.get('name')); // 'Mary'
console.log(newClient.get('age')); // 25
console.log(client.get('name')); // 'John'
console.log(client.get('age')); // 25


Sometimes, tracking mutations and maintaining a state can be challenging to handle. Working with immutable data encourages you to think differently about how data flows through your application.

Immutable collections should be treated as values rather than objects.

While objects represent something that could change over time, a value represents the state of that thing at a particular instance in time. This principle is most important to understand the appropriate use of immutable data.

Exploring the data structures

  • List: is an immutable representation of an array. This List has the main functions of a JavaScript array:

var Immutable = require('immutable');
var scores1 = Immutable.List([2, 4, 6, 8]);
console.log(scores1.size); // 4
var scores2 = scores1.push(10); // [2,4,6,8,10]
var scores3 = scores2.pop().pop(); // [2,4,6]
var scores4 = scores3.shift(); // [4,6]
var scores5 = scores4.concat(10, 12, 14); // [4,6,10,12,14]


  • Stack: this is the classic FILO (first in, last out) data structure.  To modify the stack you can only use the push() and pop() methods and to access their elements you can use the get(index) method, take a look:

var Immutable = require('immutable');
var stack = Immutable.Stack();
var scores = stack.push(10, 12, 14);
console.log(scores.size); // 3
console.log(scores.get()); // 10
console.log(scores.get(0)); // 10
console.log(scores.get(1)); // 12
console.log(scores.get(2)); // 14
var newScores = scores.pop(); // [10, 12]


  • Map: is a key-value data structure, that represents a JavaScript object, but it’s an immutable one. In the constructor, you add the key and values. To access some value, you use the map.get('key') and to change some value you use the map.set('key', newValue), but this change will generate a new Map instead of mutating the current one. We have already seen the Map in action at the beginning of this post, so let’s jump to the next one.

  • OrderedMap: this is a mix of objects and arrays this data structure can be treated as an object by using the orderedMap.get('key') and orderedMap.set('key', newValue) functions, and there are some array methods too, like orderedMap.first() and orderedMap.last() methods. The keys are ordered based on the order in which they were added to the map. And you can re-define the order of these keys by using the orderedMap.sort() and orderedMap.sortBy() methods which will return a new ordered map.


var Immutable = require('immutable'); var clients = Immutable.OrderedMap() .set('John', 25) .set('Mary', 27); console.log(clients.first(), clients.last()); // 25, 27 console.log(JSON.stringify(clients)); // '{"John": 25, "Mary": 27}' var olderClients = clients.sortBy(function(value, key) { return -value; }); console.log(JSON.stringify(olderClients)); // '{"Mary": 27, "John": 25}'

  • Set: is an immutable array of unique elements. No duplicated values are allowed, so if you add a duplicated value, the second one will be ignored.

var Immutable = require('immutable');
var set1 = Immutable.Set([1, 2, 3, 3]);
var set2 = Immutable.Set([4, 5, 5]);

console.log(set1.count()); // 3
console.log(set1.toArray()); // [1,2,3]
console.log(set2.count()); // 2
console.log(set2.toArray()); // [4,5]

var union = set1.union(set2);
console.log(union.count()); // 5
console.log(union.toArray()); // [1,2,3,4,5]


  • OrderedSet: This is a Set with keys ordered according to the time of addition, similar to OrderedMap, but it’s a Set.

var Immutable = require('immutable');
var orderedSet1 = Immutable.OrderedSet([1, 2, 2]);
var orderedSet2 = Immutable.OrderedSet([2, 1, 2]);

console.log(orderedSet1.count()); // 2
console.log(orderedSet1.toArray()); // [1,2]
console.log(orderedSet2.count()); // 2
console.log(orderedSet2.toArray()); // [2,1]

var intersected = orderedSet1.intersect(orderedSet2);
console.log(intersected.count()); // 2
console.log(intersected.toArray()); // [1,2]


  • Record: is a JavaScript class on which you can set default values. In the absence of a value, the default value will be used. This is useful to instantiate immutable objects.

var Immutable = require('immutable');
var Client = Immutable.Record({
    name: 'John',
    age: 25
});
var john = new Client();
console.log(john.toJSON()); // Object { name: 'John', age: 25 }
var mary = new Client({
    name: 'Mary',
    age: 20
});
console.log(mary.toJSON()); // Object { name: 'Mary', age: 20 }


Learning some useful functions

The immutable.js library has some useful functions that facilitate the manipulation of immutable data structures. Here are some useful modules you must learn how to use:

  • Immutable.Seq(): represents a sequence of values, but may not be backed by a concrete data structure. It allows you to run a chain of operations, see this example:

var object = Immutable.Seq({
        a: 1,
        b: 1,
        c: 1
    })
    .flip()
    .map(function(key) {
        return key.toUpperCase()
    })
    .flip()
    .toObject();
console.log(object); // Map { A: 1, B: 1, C: 1 }


  • Immutable.Range(): returns a sequence of numbers from start (inclusive) to end (exclusive), by step values. The default values for these variables are start=0, end=infinity, and step=1. When the start and end are equal values, it returns an empty range.

console.log(Immutable.Range()); // [0,1,2,3...]
console.log(Immutable.Range(5)); // [5,6,7,8...]
console.log(Immutable.Range(5, 10)); // [5,6,7,8,9]
console.log(Immutable.Range(5, 10, 2)); // [5,7,9]
console.log(Immutable.Range(5, 5)); // []


  • Immutable.Repeat(value, times): returns a sequence of values repeated by X times. When times are not defined, returns an infinite sequence.

console.log(Immutable.Repeat('john')); // ['john', 'john', 'john'...]
console.log(Immutable.Repeat('mary', 2)); // ['mary', 'mary']


Conclusion

This is a great library to handle with immutable data structures. It corrects the flaws of underscore.js and lodash libraries, namely that operations of different data structures were forced on JavaScript arrays and objects, mixing the concept of data types and losing immutability.

The name immutable.js reflects that we must deal with immutable data structures as a necessary condition for exercising pure functional programming.

Pay special attention if you're developing commercial JavaScript apps with sensitive logic. You can protect them against code theft, tampering, and reverse engineering by starting your free Jscrambler trial.

Jscrambler

The leader in client-side Web security. With Jscrambler, JavaScript applications become self-defensive and capable of detecting and blocking client-side attacks like Magecart.

View All Articles

Must read next

Cybersecurity

How to Prevent Data Leakage on Your Website

Understanding data leakage and its consequences is fundamental for anyone who manages or operates a website. This blog post aims to explore these mechanics, providing you with the knowledge needed...

June 4, 2024 | By Antonello Semeraro | 7 min read

Web Development

The Data Processing Holy Grail? Row vs. Columnar Databases

Columnar databases are great for processing big amounts of data quickly. Let's explore how they perform when compared with row DBs like Mongo and PSQL.

December 5, 2019 | By João Routar | 8 min read

Section Divider

Subscribe to Our Newsletter