Constraining our programs to manipulate immutable data structures is known to be beneficial in terms of code predictability, concurrency and safety.

In a language like Clojure, where immutable data structures are native to the language, this benefits come for free. However, in languages where immutable data structures are not native to the language, the price we have to pay to benefit from data immutability is that we need to convert the data back and forth from immutable to native. Beside the inconvenience of this conversion, it might cause a performance hit, it we are not careful with the conversion.

Car

The purpose of the is article is to illustrate how to manipulate carefully immutable data collection from Immutable.js with the a data manipulation library like Lodash.js, leveraging Immutable’s shallow and deep conversion functions.

A similar approach could be applied to other immutable data collection libraries and other data manipulation libraries (and also to other programming languages).

Imagine we have a nested piece of data that we want to:

  1. Treat as immutable (e.g Immutable.js)
  2. Manipulate with data manipulation library (e.g Lodash.js)

The challenge is that Immutable.js data collections are not native JavaScript objects. Therefore, before passing the data to a JavaScript library, we have to convert it to a JavaScript object. In this article, we are going to show how to convert to JavaScript without impacting the performance too much.

Let’s take as an example the data of a library, that might looks similar to this nested object:

var libraryData = {
  "name": "The smallest library on earth",
  "address": "Here and now",
  "catalog": {
    "books": [
      {
        "title": "Watchmen",
        "publicationYear": 1986,
        "authors": [
          {
            "firstName": "Alan",
            "lastName": "Moore"
          },
          {
            "firstName": "Dave",
            "lastName": "Gibbons"
          }
        ]
      },
      {
        "title": "Jimmy Corrigan, the Smartest Kid on Earth",
        "publicationYear": 2000,
        "authors": [
          {
            "firstName": "Chris",
            "lastName": "Ware"
          }
        ]
      },
      {
        "title": "Ultimate Spider-Man",
        "publicationYear": 2000,
        "authors": [
          {
            "firstName": "Brian Michael",
            "lastName": "Bendis"
          }
        ]
      }
    ]
  },
  "users": [
    {"username": "user-1"}, {"username": "user-2"}, {"username": "user-3"}
  ]
}

Now, we convert the native JavaScript object to an Immutable map with fromJS():

var immutableLibData = Immutable.fromJS(libraryData);

Our purpose is to find the best way to call _.countBy() on our immutable collection, in the same way as we would use it on the JavaScript native object:

_.countBy(libraryData.catalog.books, "publicationYear");

Immutable.js provides a .toJS() function that deeply converts an immutable collection to a JavaScript object or array.

We are then free to pass immutableLibData.toJS() to any Lodash function:

_.countBy(immutableLibData.toJS().catalog.books, "publicationYear");

The problem is that it causes a performance hit, as we have to convert the whole immutableLibData immutable collection (including the users part which is unnecessary)

A better solution is to convert only the part of the data that we are interested in:

_.countBy(immutableLibData.getIn(["catalog", "books"]).toJS(), "publicationYear");

But still, there is a performance hit as we convert the books data deeply (including the authors part which is unnecessary).

The best thing we can do, is to do a careful shallow conversion to native JavaScript: It requires 2 steps:

  1. Shallow convert books to a JavaScript array with Immutable’s toArray()
  2. Shallow convert each object of the books array to a JavaScript object with Immutable’s toObject()

Here is the code for that:

function toArrayofObjects(m) {
  return m.toArray().map(x => x.toObject());
}

And now, we can use _.countBy() with no unnecessary performance hit:

_.countBy(toArrayofObjects(immutableLibData.getIn(["catalog", "books"])), "publicationYear");

A similar approach could be applied to other immutable data collection libraries and other data manipulation libraries (and also to other programming languages). The features that the immutable data collection library needs to provide are:

  1. Deep conversion to native objects (like Immutable toJS())
  2. Shallow conversion to native objects (like Immutable toArray() and toObject())

Enjoy immutability!

This article is an excerpt from my book about Data-Oriented Programming.

More excerpts are available on my blog.