To Mutate, Or Not To Mutate? JavaScript Array Methods Cheat Sheet
Annie Liao
Posted on February 3, 2020
When you start learning different ways to manipulate an array, your world is lit up. You can add and delete elements! You can reverse them! Then you realize that not all built-in array methods behave the way you want them to. Somewhere along the way, you get confused as to which method alters your original array, and which one doesn’t.
Wouldn’t it be nice to have a tool that tells you if a particular array method mutates the original array or not?
Yes, I know. We can always check MDN documentation. Better yet, there’s this awesome website that lists (hopefully) all commonly-used array methods and identifies them as “mutates” or “doesn’t mutate”.
I did a quick tally and found that out of the 31 methods listed on the site, only 9 of them mutate the array.
Here's an idea:
Why not just remember the 9 destructive array methods? That way, we need not pause too long when it comes to the manipulation of arrays. If we call any of the 9 methods, the original array will be modified for sure.
Alright, let's explore these 9 methods and watch the mutators mutate!
#1 .push()
The .push() method takes in an array element, which will be added to the end of the array.
Here we have an array called "happy", consisting of 3 happyface emojis. After we push a poop face, the "happy" array now has a happy poopy face attached to the end.
let happy = ['🙂', '😀', '😊'];
happy.push('💩');
happy
// => [ '🙂', '😀', '😊', '💩']
#2 .pop()
This method is similar to .push() method, as both involve changing the last item of the array.
Contrary to .push() method, .pop() will remove the last item of the array. As shown below, we just .pop() the poop out of the happy array!
happy
// => [ '🙂', '😀', '😊', '💩']
happy.pop();
happy
// => [ '🙂', '😀', '😊']
👉Tip: The poop emoji was not randomly chosen. It is here to help us remember that both .push() and .pop() mutate the array by adding (pushing) or removing (popping) the last item (poop).
#3 .unshift()
The .unshift() method works similarly to .push(), except that it adds a new item to the beginning of the array.
Back to our clean, poop-free happyface example. After we pass in a ghost emoji onto the .unshift() method, the happy array is now led by a ghost!
let happy = [ '🙂', '😀', '😊']
happy.unshift('👻');
happy
// => [ '👻', '🙂', '😀', '😊']
Now, what if we want to remove the first item?
#4 .shift()
Whereas .pop() removes the last item, .shift() simply removes the first item in an array, like so:
happy
// => [ '👻', '🙂', '😀', '😊']
happy.shift();
happy
// => ['🙂', '😀', '😊']
👉Tip: As you might have noticed by now, .unshift() and .shift() are mirroring methods that add/remove the first item in an array. By the same token, you can think of .push() and .pop() as another set of methods that add/remove the last item of an array.
#5 .reverse()
This one is a no-brainer. As its name suggests, the .reverse() method reverses the order of the elements in an array. Here, we can see a waxing and waning moon effect thanks to the .reverse() method.
let moon = ['🌒', '🌓', '🌔', '🌕'];
moon.reverse();
moon
// => [ '🌕', '🌔', '🌓', '🌒']
#6 .splice()
The .splice() method is powerful, as it can take as many arguments as you want and mutate the array by adding and/or replacing item(s) within the array.
In the three-wise-monkeys array example below, we implement the .splice() method by passing in 3 types of arguments:
(1) the index at which the change starts (index 1)
(2) number of items to remove (1 item: "hear-no-evil" monkey)
(3) item(s) to add to the array (3 evil faces)
let monkeys = ['🙈', '🙉', '🙊'];
monkeys.splice(1, 1, '😈', '😈', '😈');
Now the "hear-no-evil" monkey is gone and replaced by 3 evil faces 😱
monkeys
// => [ '🙈', '😈', '😈', '😈', '🙊']
You can also choose to only add items to a specific location inside the array. Here, we inserted the evil face at index 2 but kept all three monkeys (by passing in 0 as the second argument).
let monkeys = ['🙈', '🙉', '🙊'];
monkeys.splice(2, 0, '😈');
monkeys
// => [ '🙈', '🙉', '😈', '🙊']
👉Tip: Watch out for the .slice() method. It looks like .splice() on the surface, but .slice() method produces a new array and hence does NOT mutate the original array.
#7 .sort()
This one is a doozy. The .sort() method does sort the items in the array in descending order by default, but it doesn't necessarily behave the way we commoners expect.
Let's say we have an array of numbers. What happens if we apply the .sort() method?
let numbers = [3, 25, 99];
numbers.sort();
numbers
// => [ 25, 3, 99 ]
Weird, right?
As it turns out, the .sort() method converts each item into a string and compares them according to their Unicode code points.
We can check the Unicode code points of the first value of a string by calling the .codePointAt() method.
After coercing these numbers into strings, we see why .sort() considers "25" less than "3":
"3".codePointAt(0)
// => 51
"25".codePointAt(0)
// => 50
"99".codePointAt(0)
// => 57
Let's not forget that emojis are also passed in as strings!
let madness = ["🤬", "😠", "😡"]
madness.sort();
madness
// => [ '😠', '😡', '🤬' ]
"😠".codePointAt(0)
// => 128544
"😡".codePointAt(0)
// => 128545
"🤬".codePointAt(0)
// => 129324
To top it off:
let mixMadness = [3, "🤬", 25, "😠", "😡", "wtf"]
mixMadness.sort();
mixMadness
// => [ 25, 3, 'wtf', '😠', '😡', '🤬' ]
/*** behind the scenes ***/
"25".codePointAt(0)
// => 50
"3".codePointAt(0)
// => 51
"wtf".codePointAt(0)
// => 119
"😠".codePointAt(0)
// => 128544
"😡".codePointAt(0)
// => 128545
"🤬".codePointAt(0)
// => 129324
If you really want to sort the numbers in an order that makes human sense, you can pass in a compare function as an argument in the .sort() method:
let numbers = [25, 99, 3];
numbers.sort(function(num1, num2) {
return num1 - num2;
});
numbers
// => [ 3, 25, 99 ]
In the code snippet above, the .sort() method takes in a function that compares two adjacent numbers in the array. Here's a breakdown of what happens:
(1) if the result of (num1 - num2) is less than 0, num1 will precede num2.
(2) if the result of (num1 - num2) is greater than 0, num2 will take precedence.
(3) if the result of (num1 - num2) is 0, the order of num1 and num2 will remain the same.
(big thanks to @jamiemccarville for the explainer reminder!)
And if your head hasn't exploded yet, check out this amazing article that dives deep into the .sort() method: What I Talk About When I Talk About Sorting: Untangling Array#sort
#8 .copyWithin()
As the name suggests, this method copies one part of the array and put it on another part within the same array.
It takes in 3 arguments:
(1) the index at which to put copied item(s)
(2) (optional) the index at which to start copying item(s) from (inclusive)
(3) (optional) the index at which to end copying item(s) from (exclusive)
To illustrate, here's how we can utilize the .copyWithin() method:
let okHands = ["👌", "✋", "🤙", "🙏"]
okHands.copyWithin(2, 0, 1)
/* arg 1: put copied item at index 2, which is currently "🤙" */
/* arg 2 & 3: copy the item between index 0 (inclusive) and index 1 (exclusive), which is currently "👌"
// => [ '👌', '✋', '👌', '🙏' ]
Here's another example to help clarify the purpose of each argument:
okHands = ["👌", "✋", "🤙", "🙏"]
okHands.copyWithin(2, 0, 2)
/* arg 1: put copied item at index 2, which is currently "🤙" */
/* arg 2 & 3: copy the item between index 0 (inclusive) and index 2 (exclusive), which are currently "👌", "✋"
// => [ '👌', '✋', '👌', '✋' ]
Notice how the last item ("🙏") was replaced after we copied two items ("👌", "✋").
While I haven't had an opportunity to use .copywithin() in my program, this method appears to function similarly to .splice(). Based on the types of arguments both methods take in, the latter offers more flexibility because we can insert new items instead of copying from within. So, pick your poison.
#9 .fill()
Lastly, the .fill() method changes some or all items in the array into the value being passed in.
It takes in 3 arguments as well:
(1) a value to fill the array with
(2) (optional) the starting index (inclusive)
(3) (optional) the ending index (exclusive)
For example, you can fix a broken heart:
let spreadTheLove = ["<3", "💔", "love", "heart"];
spreadTheLove.fill("❤️", 1, 2);
spreadTheLove
// => [ '<3', '❤️', 'love', 'heart']
Or, just fill them all with luuuve:
spreadTheLove = ["<3", "💔", "love", "heart"];
spreadTheLove.fill("❤️");
spreadTheLove
// => [ '❤️', '❤️', '❤️', '❤️' ]
One distinguishing feature of .fill() method is that it only takes a single, static value. If you want to insert multiple values into an array, look elsewhere.
Recap
Again, here are the 9 essential array methods that mutate the original array:
.push() // => adds a new item as the last item of the array
.pop() // => removes the last item of the array
.unshift() // => adds a new item as the first item of the array
.shift() // => removes the first item of the array
.reverse() // => reverses the order of the array
.splice() // => removes/replaces item(s) in the array
.sort() // => re-orders the items in the array based on their Unicode code points
.copyWithin() // => copies one part of the array and put it on another part of the same array
.fill() // => changes some or all items in the array into the value being passed in
By remembering these 9 methods I was able to expedite my coding process; it has also alleviated my worry of accidentally copying or mutating the array. Hope you find it useful, too!
Posted on February 3, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.