How to Merge a List of Maps by Key in Immutable.js
Jan 9, 2020 • 8 Minute Read
Introduction
In this guide, we'll learn how to write immutable code using Immutable.js. Immutability is a critical aspect of writing accurate code and must be implemented by teams working on large codebases. We'll see how immutability can be beneficial and take a look at Immutable.js and its Map data structure. But before that, let's take a look at why is it necessary to write immutable code.
Mutations
Mutation, in general, means a change from an original form. If something is mutable, it can be changed. The same principle applies to JavaScript when you change or add a property to an existing object.
Consider the below example.
const Person = { name: "John", age: 32 };
console.log("Before mutation - ", Person);
Person.age = 35;
console.log("After mutation - ", Person);
When we run this code in the console, we get the following output:
As you can see, the age property of the Person object was changed, and there's no way we can get back the original object. This is called a mutation.
Similarly, mutations can be done on an array.
const languages = ["JavaScript", "PHP", "Java", "C#"];
console.log("Before mutation - ", languages);
languages.pop();
console.log("After mutation - ", languages);
Immutability
An object is said to be immutable when its properties cannot be changed once initiated. Unfortunately, JavaScript does not have immutable data structures natively, and it's up to the programmer to write immutable code.
To demonstrate this, I'm going to rewrite the above code in an immutable manner.
const Person = { name: "John", age: 32 };
console.log(Person);
const MutatedPerson = Object.assign({}, Person, { age: 35 });
console.log(Person);
console.log(MutatedPerson);
The Object.assign() method combines two or more objects into a single one. The first parameter is the target object to which you want to combine the objects. And the rest of the parameters are the source object, each overriding the other source.
This way, we can track the values of an object and make it easier for ourselves to debug the code. But this practice doesn't guarantee that someone else could not mutate the object. That's why it is vital to use immutable data structures; hence, Immutable.js was built by the Facebook Team.
Intro to Immutable.js
Immutable.js is a library that creates a collection of data, which, once initialized, cannot be changed or mutated.
This is how Immutable.js describes Immutable data:
Immutable data cannot be changed once created, leading to much simpler application development, no defensive copying, and enabling advanced memorization and change detection techniques with simple logic. Persistent data presents a mutative API which does not update the data in-place, but instead always yields new updated data.
The data collections are based on JavaScript's Array, Map, and Set objects, but with a significant difference that any mutation is done on a copy of the original object.
For example, the set() method, which sets the property of a Map data collection, actually creates a new Map collection and mutates the property, leaving the original Map collection untouched.
Maps
Let's take a look at the Map data structure in more detail.
An Immutable Map is an unordered collection of key-value pairs that are based on JavaScript's object.
Let's create a very basic Map collection.
import { Map } from "immutable";
const myMap = Map();
We import Map constructor from immutable, then call the constructor and assign it to a variable. This is how a very basic Map can be created without any properties or key-value pairs.
To add properties, we pass an object of key-value pairs in the Map() function.
import { Map } from "immutable";
const Person = Map({ name: "John", age: 32 });
To change the property, the Map collection has a set() method, which takes in a key as the first argument and the value as the second, and returns a new Map collection without mutating the original collection.
const MutatedPerson = Person.set("age", 35);
Now, if we console log the above collections, we can see two different values.
console.log(Person);
console.log(MutatedPerson);
Merging Maps by Keys
There are several ways to merge Maps.
merge() Method
We can merge Map collections using the merge() method. This returns a copy of the collection with the remaining collections merged in.
import { Map, merge } from "immutable";
const Person = Map({ name: "John", age: 32 });
const MutatedPerson = merge(Person, { age: 35 });
console.log(Person);
console.log(MutatedPerson);
We get the same results as the previous example when we run the above code.
The merge() method can also be used to merge Map collections.
import { Map, merge } from "immutable";
const Map1 = Map({ a: 29, b: 88, c: 56 });
const Map2 = Map({ a: 38, b: 45, z: 99 });
const MergedMap = merge(Map1, Map2);
console.log("Map1 - ", Map1);
console.log("Map2 - ", Map2);
console.log("MergedMap - ", MergedMap);
As you can see, the properties of the preceding Map collection were overridden by the following collection, and the unique key-value pairs got added to the merged Map collection. Once again, the original Map collections were not mutated and are untouched.
mergeWith()
The mergeWith() function allows us to handle the existing key-value pairs by passing in a merger function as the first parameter, rather than just overriding the existing value.
For example, let's say we have to add the values of merging keys.
import { Map, mergeWith } from "immutable";
const Map1 = Map({ a: 29, b: 88, c: 56 });
const Map2 = Map({ a: 38, b: 45, z: 99 });
const MergedMap = mergeWith(
(oldValue, newValue) => oldValue + newValue,
Map1,
Map2
);
console.log("Map1 - ", Map1);
console.log("Map2 - ", Map2);
console.log("MergedMap - ", MergedMap);
mergeDeep
The mergeDeep() function is similar to the merge() function except it can merge the collections recursively.
For example, consider the below maps.
const Map1 = Map({ a: 29, b: 88, c: 56, d: { da: 77 } });
const Map2 = Map({ a: 38, b: 45, z: 99, d: { db: 96 } });
If we use the merge() function, the key d will simply be overridden.
const Map1 = Map({ a: 29, b: 88, c: 56, d: { da: 77 } });
const Map2 = Map({ a: 38, b: 45, z: 99, d: { db: 96 } });
const MergedMap = merge(Map1, Map2);
console.log("MergedMap - ", MergedMap);
Now let's take a look when we use the mergeDeep() function.
const Map1 = Map({ a: 29, b: 88, c: 56, d: { da: 77 } });
const Map2 = Map({ a: 38, b: 45, z: 99, d: { db: 96 } });
const MergedMap = mergeDeep(Map1, Map2);
console.log("MergedMap - ", MergedMap);
The values of the key d were merged to form a new value that is assigned in the MergedMap .
Conclusion
Immutable.js is a great library to use when building complex web applications. Merging Map collections is very easy with immutable functions like merge(), mergeWith(), and mergeDeep(). These methods come very handy when dealing with API responses.
So, that's it from this guide. If you have any queries regarding this topic, feel free to contact me at CodeAlphabet. Happy coding and cheers!