DEV Community

Kishore
Kishore

Posted on

Mutate object or arrays without changing original source in javascript

rctx-contextmenu
Photo by Luca Micheli on Unsplash

If you are building javascript application irrespective of any framework, you would have faced a specific issue where you want to modify an Object or Array, but with modification, the original value also updating, but you don't want to update the original value.

That's where an immutability helpers libraries help us. I personally prefer Immutability Helper library to handle this but there are others like Immutable JS, which is also a great library.

In this post, I will be discussing the Immutability Helper library and will try to cover most of the features like push, splice, merge, etc.

Prerequisites

Before starting I would like you to go through MDN specification regarding data types and data structures which will give you a fair idea on various kinds of data types and data structures in JS.

The problem

const originalObj = {
  name: 'Volkswagen',
  color: 'red'
}

const newObj = originalObj;

newObj.color = 'green';

console.log(newObj.color); // green
console.log(originalObj.color); // green
Enter fullscreen mode Exit fullscreen mode

The above example looks strange because I have changed newObj color value but originalObj color value also got the changes. This weird thing is happening because in Javascript Object and Array are reference types, that means if you are creating an object or array in one place and then assigning that value to another variable it takes only one memory allocation. So if you are changing any property value of newObj which has a reference value of originalObj, that means you are changing in only one place. This happens the same for Arrays as well.

More on this you can find here.


Now coming to our main focus area which is how to make object or arrays immutable using Immutability Helper library.

To use Immutability Helper you have to first install it by running the below command

npm install immutability-helper --save
Enter fullscreen mode Exit fullscreen mode

and import it wherever you want to use

import update from 'immutability-helper';
Enter fullscreen mode Exit fullscreen mode

Below are some of the commands which we can use

$push

push() all the items in array on the target

Example:

const initialArray = [1, 2, 3];
const newArray = update(initialArray, {$push: [4]});

console.log(initialArray); // [ 1, 2, 3 ]
console.log(newArray); // [ 1, 2, 3, 4 ]
Enter fullscreen mode Exit fullscreen mode

$splice

For each item in arrays call splice() on the target with the parameters provided by the item

Nested array push example:

const collection = [1, 2, {a: [12, 17, 15]}];
const newCollection = update(collection, {2: {a: {$splice: [[1, 1, 13, 14]]}}});

console.log(collection); // [ 1, 2, { a: [ 12, 17, 15 ] } ]
console.log(newCollection); // [ 1, 2, { a: [ 12, 13, 14, 15 ] } ]
Enter fullscreen mode Exit fullscreen mode

Remove array element example:

const obj = {items: [1, 2, 3, 4]};
const index = 2;
const newObj = update(obj, { items: { $splice: [[index, 1]] } });

console.log(obj); // { items: [ 1, 2, 3, 4 ] }
console.log(newObj); // { items: [ 1, 2, 4 ] }
Enter fullscreen mode Exit fullscreen mode

$apply

Passes in the current value to the function and updates it with the new returned value

const obj = {a: 5, b: 3};
const newObj = update(obj, {b: {$apply: function(x) {return x * 2;}}});

console.log(obj); // { a: 5, b: 3 }
console.log(newObj); // { a: 5, b: 6 }
Enter fullscreen mode Exit fullscreen mode

$set

Replace the target entirely

Simple example:

const obj = {a: 5, b: 3};
const newObj = update(obj, {b: {$set: obj.b * 2}});

console.log(obj); // { a: 5, b: 3 }
console.log(newObj); // { a: 5, b: 6 }
Enter fullscreen mode Exit fullscreen mode

Example with computed property names:

const collection = {children: ['zero', 'one', 'two']};
const index = 1;
const newCollection = update(collection, {children: {[index]: {$set: 1}}});

console.log(collection); // { children: [ 'zero', 'one', 'two' ] }
console.log(newCollection); // { children: [ 'zero', 1, 'two' ] }
Enter fullscreen mode Exit fullscreen mode

$merge

Merge the keys of an object with the target

const obj = {a: 5, b: 3};
const newObj = update(obj, {$merge: {b: 6, c: 7}});

console.log(obj); // { a: 5, b: 3 }
console.log(newObj); // { a: 5, b: 6, c: 7 }
Enter fullscreen mode Exit fullscreen mode

There are other 2 advanced features. One is Autovivification and another one is Adding your own commands. You can check out those from their respective docs.

Conclusion

There are other libraries that solve immutability in a great way. I personally know about immutable-js, which is great in its own way. You can try both and find which suits your project best. I personally used Immutability Helper multiple times and thus I'm comfortable with this library. You can try whatever you want.

Thanks for reading!

Top comments (2)

Collapse
 
luisflins profile image
Luis Fernando Lins

Nice post! I'd also like to add that, if you don't want to add a new library just for that, you could use the spread operator, such as const newObj = {...originalObj}; and then, every time you change newObj, it won't affect originalObj anymore, as the spread operator makes a copy of the original object. The only caveat would be if you had other objects or arrays contained in the original object, and then the spread operator would have to be used in these elements as well.

Collapse
 
reachtokish profile image
Kishore

Thanks for reading!! You said it correctly about the nested thing for spread operator. I too like the spread operator and use it mostly but for the complex operations like the nested object you pointed, I use immutability-helper.