Most JavaScript beginners move fast breaking things while learning data structuresin JavaScript,stumbling on the bad parts of the language. Javascript is a dynamic and versatile programming language. easy to learn but understanding its deeper intricacies requires time and practice.
In JavaScript, arrays are an object type that contain unordered elements and can be modified once they are declared. There are numerous array methods (built-in functions) available in arrays for performing operationsincluding: sort, slice, reduce, filter, map, splice,some,find etc. There is even an entire lodash library with other helpful array methods.
Basically, an array method should not change or mutate the original array declaration instead it should always return a duplicate of the original.
For example, the following filter
should only return numbers more than five without modifying the original array.
let nums=[1,2,3,4,5,6,7,8,9,10]
let moreThanFive=nums.filter(num=>num>5)
// [6, 7, 8, 9, 10]
console.log(nums)
// [1,2,3,4,5,6,7,8,9,10]
As you can see, we have created a copy of array moreThanFive
with numbers more than 5 without modifying the original nums array.
However there are useful array methods that does the job but also cause side effects by modifying the original array. That alone causes unnecessary side effects in our app; application performs poorly.
Lets look at reverse method which should basically get a copy of nums in reverse wuthout modifying it.
let nums=[1,2,3,4,5,6,7,8,9,10]
Let reverseNum=nums.reverse()
// [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
console.log(nums)
// [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
What happened? Original array nums
is also reversed! Why? It turns out some Javascript array methods modify an original array instead of simply grabbing a copy and that is detrimental in programming. Splice, Sort, and Reverse are three effective Javascript array methods that modify an original array. Let us examine them and explore ways to use them without modifying the original array.
I want you to pay attention to changes in the original variable nums; our objective is keeping the original array unmodified.
Splice
Splice ,as the name suggests, splits a section of array and optionally inserts an array element. It takes 3 arguments; the first one is required the rest are optional,
1.The index where splice should start
let nums=[1,2,3,4,5,6,7,8,9,10]
let spliced = nums.splice(4) // get elements from index 4 to end
console.log(nums) // [1, 2, 3, 4]
//5 to 10 have been removed
2.The number of array elements to be removed (optional)
let nums=[1,2,3,4,5,6,7,8,9,10]
let let spliced = nums.splice(4,2) // get 2 elements from index 4 onwards
console.log(nums) // [1, 2, 3, 4, 7, 8, 9, 10]
// 5,6 have been removed
3.Array elements to add from where you splice (optional)
let spliced = nums.splice(4,2,10,15) // get 2 elements from index 4 then add 10,15
console.log(nums) // [1, 2, 3, 4, 10, 15, 7, 8, 9, 10]
// 5 and 6 have been removed but 10 and 15 have been added.
As you can see this method is really useful in creating new arrays but destroys the original array.
The fix
1.The old way; using array slice() method
The slice Method is friendly method that preserve the original array and creates a copy. Combining it with Splice does the same as above but original array remain unmodified.
Calling the slice method without arguments creates and return a copy of original array then splice that copy like so
let nums=[1,2,3,4,5,6,7,8,9,10]
let spliced = nums.slice().splice(4) // get elements from index 4 to end
console.log(nums) // [1,2,3,4,5,6,7,8,9,10] //nums is untouched
let nums=[1,2,3,4,5,6,7,8,9,10]
let let spliced = nums.slice().splice(4,2) // get 2 elements from index 4 onwards
console.log(nums) // [1, 2, 3, 4, 7, 8, 9, 10] //nums is untouched
2.ES2023 toSpliced() method
EcmaScript 2023 aka ES2023 introduced the array toSpliced() method which simply splices an original array and return a copy without modifying it.
let nums=[1,2,3,4,5,6,7,8,9,10]
let spliced = nums.slice().toSpliced(4) // get elements from index 4 to end
console.log(nums) // [1,2,3,4,5,6,7,8,9,10] //nums is untouched
Sort
The sort method arranges elements in ascending or descending order depending on the data type.
Let's shuffle and sort our numbers array. I'll just use my hands to shuffle because Javascript, unlike PHP, doesn't provide a shuffle array function.
let nums=[1,7,3,10,5,6,2,8,9,4]
let sorted=nums.sort()
console.log(nums) // [1, 10, 2, 3, 4, 5, 6, 7, 8, 9]
Our original array nums is modified even though the sort() method failed miserably.
It turns out Javascript has a problem sorting arrays, so we have to pass in a callback function telling it how to sort our array.
let nums=[1,7,3,10,5,6,2,8,9,4]
let sorted=nums.sort(((a,z)=>a-z) //ascending order
console.log(nums) // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] // It modified nums but sorted correctly
The fix
1.The old way; using array slice
We slice the whole original array using the slice method once more, then we sort it explicitly.
let nums=[1,7,3,10,5,6,2,8,9,4]
let sorted=nums.slice().sort() // [1, 7, 3, 10, 5, 6, 2, 8, 9, 4]
Mission accomplished, nums is untouched but sorted array is incorrectly sorted, can you figure out how to do that?
2.ES2023 toSorted() method
ES2023 added another Javascript array method,a friendly sister to sort, toSorted() sorts an array without modifying the original.
let nums=[1,7,3,10,5,6,2,8,9,4]
let sorted=nums.toSorted((a,z)=>a-z) // ascending order
console.log(nums) // [1, 7, 3, 10, 5, 6, 2, 8, 9, 4] // untouched
console.log(sorted) // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] //sorted correctly
Reverse
Array reverse method does what it does, it reverses an array forward-back or back-forward but it leaves the original array in wrecked state.
let nums=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
let reversed=nums.reverse()
console.log(nums) // [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
As you can see nums is also reversed, let fix that.
the fix
1.The slice method
The slice method is a must have tool in javascript, be it strings or arrays, it copies and leaves no damage behind.
let nums=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
let reversed=nums.slice().reverse()
console.log(nums) // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
2.ES2023 toReversed() method
EcmaScript 2023 introduced a polite toReversed method, a friendly sister of reverse
et nums=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
let reversed=nums.toReversed()
console.log(nums) // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
console.log(reversed) // [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
Don’t break the internet!!
Be careful not to break the internet as ES2023 features are supported by latest browser versions; Google Chrome 110 and above support it, latest versions of Mozilla,Safariand edge offer support.
Here is the error that occurs when you try toReversed()
on Mozilla Firefox version 78.
The work around for this scenario is to run feature detection on the browser before using the feature else fallback to the old ways.You that by checking if Array prototype
has the new method.
if(Array.prototype.toReversed){
console.log(" toReversed() is supported, use ES2023")
let nums=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
let reversed=nums.slice().reverse()
}else{
console.log(" toReversed() is not supported, use the old tricks")
let nums=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
let reversed=nums.slice().reverse()
}
A word on React Library
React, a JavaScript UI library, understands the impact of this methods on data transformation and thus uses map,filter and reduce
methods incessantly as workhorses as they do not have side effect to app performance.
In conclusion, some array methods perform the expected functions but also give unusual side effect which can hurt the performance of an application.
Top comments (1)
Sure there is a place for returning different arrays rather than mutating them sometimes, perhaps much of the time for small arrays... A word of caution however: if you use this functionality blindly in all circumstances you'll totally kill performance. Allocating vast amounts of memory again and again is inefficient and wholly the wrong strategy for a significant minority of real world cases.