How to Avoid Array Mutation

Round purple puzzle piece becoming a square purple puzzle piece
Summary
  • The post explains the difference between mutating and non-mutating approaches to modifying JavaScript arrays.
  • Mutating methods like push, pop, splice, and direct index assignment alter the original array in place, which can lead to bugs and unpredictable behavior.
  • Non-mutating techniques using concat, slice, spread syntax, map, and filter always return a new array reference, preserving the original data.
  • Avoiding mutation leads to safer, more predictable code and aligns with functional programming principles such as immutability and unidirectional data flow.

Originally posted on dev.to

In this article, I’ll focus on showing how to add, edit and remove items in an array causing mutation and non-mutation ways.

One thing we need to keep in mind when writing code avoiding mutation is to return a new reference to the data after the update.

It’s a common approach when working with functional programming and if you want to understand some concepts of functional programming I recommend you read this article I wrote some time ago.

Why Avoid Mutation

When you work with immutable data you can have some positive impacts like the following:

– Tracking data without mutation is much better;

– Immutable states help you implement unidirectional data flow that helps you handle data;

I really recommend you read this article to go deeper into why avoid mutation.

Causing Mutation

The following steps will cause mutation into the array when adding, removing and editing elements from `family`.

To show an example of mutating, we’ll use the following array:

const heroesMutate = ['Spider-man', 'Thor', 'Hulk', 'Iron Man'];
console.log(heroesMutate); // => ["Spider-man", "Thor", "Hulk", "Iron Man"]

Including in Array

Methods that will be used:

See the following use-case examples for these methods:


heroesMutate.push('Captain Marvel');
console.log(heroesMutate); // => ["Spider-man", "Thor", "Hulk", "Iron Man", "Captain Marvel"]

heroesMutate.unshift('Deadpool');
console.log(heroesMutate); // => ["Deadpool", "Spider-man", "Thor", "Hulk", "Iron Man", "Captain Marvel"]

heroesMutate.splice(2, 0, 'Black Panther');
console.log(heroesMutate); // => ["Deadpool", "Spider-man", "Black Panther", "Thor", "Hulk", "Iron Man", "Captain Marvel"]

 

Editing the Array

The following case will find index for the element we want to edit and set value to the index found:


const indexDeadpool = heroesMutate.indexOf('Deadpool');
heroesMutate[indexDeadpool] = 'Wolverine';

console.log(heroesMutate); // => ["Wolverine", "Spider-man", "Black Panther", "Thor", "Hulk", "Iron Man", "Captain Marvel"]

 

Removing in the Array

Methods that will be used:

See the following use-case examples for these methods:


heroesMutate.pop();
console.log(heroesMutate); // => ["Wolverine", "Spider-man", "Black Panther", "Thor", "Hulk", "Iron Man"]

heroesMutate.shift();
console.log(heroesMutate); // => ["Spider-man", "Black Panther", "Thor", "Hulk", "Iron Man"]

heroesMutate.splice(1, 1);
console.log(heroesMutate); // => ["Spider-man", "Thor", "Hulk", "Iron Man"]

 

Avoiding Mutation

In this topic, we’ll add, remove and edit, avoiding mutations.

Methods that will be used:

See the following use-cases:


const villains = ['Loki', 'Thanos', 'Venom', 'Abomination'];

 

Including in the Array

Add to the end of array:


const newVillains = villains.concat('Juggernaut');
const newVillains2 = [...newVillains, 'Magneto'];
const newVillains3 = ['Red Skull', ...newVillains2];

console.log(villains); // => ["Loki", "Thanos", "Venom", "Abomination"]
console.log(newVillains); // => ["Loki", "Thanos", "Venom", "Abomination", "Juggernaut"]
console.log(newVillains2); // => ["Loki", "Thanos", "Venom", "Abomination", "Juggernaut", "Magneto"]
console.log(newVillains3); // => ["Red Skull", "Loki", "Thanos", "Venom", "Abomination", "Juggernaut", "Magneto"]

 

In the following example we’ll add Ultron  after Thanos in the array:


const newVillains = [...villains.slice(0, 2), 'Ultron', ...villains.slice(2, villains.length)];

console.log(villains); // => ["Loki", "Thanos", "Venom", "Abomination"]
console.log(newVillains); // => ["Loki", "Thanos", "Ultron", "Venom", "Abomination"]

 

Editing the Array

In the following example we’ll edit `Venom` to `Galactus`:


const indexVenom = villains.indexOf('Venom');
const newVillains = [...villains.slice(0, indexVenom), 'Galactus', ...villains.slice(indexVenom+1)];
const newVillains2 = newVillains.map(v => v === 'Abomination' ? 'Ultron' : v);

console.log(villains); // => ["Loki", "Thanos", "Venom", "Abomination"]
console.log(newVillains); // => ["Loki", "Thanos", "Galactus", "Abomination"]
console.log(newVillains2); // => ["Loki", "Thanos", "Galactus", "Ultron"]

 

Removing in the Array

In the following example we’ll remove Thanos from the array:


const indexThanos = villains.indexOf('Thanos');
const newVillains = [...villains.slice(0, indexHelder), ...villains.slice(indexHelder+1)];
const newVillains2 = newVillains.filter(v => v !== 'Thanos');

console.log(villains); // => ["Loki", "Thanos", "Venom", "Abomination"]
console.log(newVillains); // => ["Loki", "Venom", "Abomination"]
console.log(newVillains2); // => ["Loki", "Abomination"]

See that in all the examples that we developed above, a new instance of the array was created, thus avoiding the mutation of the initially defined arrays.

 

Wrapping Up

Avoiding mutations is a safe and one-way path.

When you realize that you’re writing code observing this type of detail, believe me, you will be writing better, secure code and avoiding possible bugs due to mutation.

Feel free to share your feedback and experience in the comments.

Enjoy programming! ✨

References

FAQ

What is array mutation and why should it be avoided?

Array mutation means directly modifying the original array in place. Avoiding mutation is beneficial because it makes data tracking easier, supports immutable states, and enables unidirectional data flow, which helps prevent bugs and leads to safer, more predictable code.

Which JavaScript methods cause array mutation?

Methods that cause mutation include Array.prototype.push() and unshift() for adding elements, Array.prototype.pop() and shift() for removing elements, and Array.prototype.splice() for adding, removing, or replacing elements at specific positions.

Which JavaScript methods allow array operations without causing mutation?

Non-mutating methods include Array.prototype.concat(), slice(), map(), and filter(), as well as the spread syntax (...). These methods return a new array reference instead of modifying the original.

How can you add an element to an array without mutating it?

You can use concat() to add to the end, or use the spread syntax combined with slice() to insert at a specific position. For example: const newArray = [...original.slice(0, index), newItem, ...original.slice(index)] creates a new array without altering the original.

How can you remove or edit an element in an array without mutation?

To remove an element, use filter() or combine slice() with spread syntax to skip the unwanted index. To edit, use map() to replace a matching value, or use slice() with spread syntax to replace the element at a specific index, always returning a new array.

About the author.

Helder Burato Berto
Helder Burato Berto

I keep moving constantly in search of knowledge, improve the quality of work and also as a person.