JavaScript: Array, mutability & immutability
length()
- The length property is used to find out the size of that object.
- It’s used with many objects like JavaScript string, JavaScript array, etc.
let arr = ['Sparrow', 'Ostrich', 'Hawk', 'Parrot', 'Seagull', 'Blackbird', 'Penguin', 'Woodpecker']console.log(arr.length);
// 8let arrString = ‘helloWorld’;console.log(arrString.length)
// 11
- Every Array object has a length property whose value is always a non-negative integer less than 2²³ (i.e; 4294967296)
let arr = new Array(4294967296)
console.log(arr.length);
// RangeError: Invalid array lengthlet arr1 = new Array(-100)
console.log(arr1.length);
// RangeError: Invalid array length
In the above code, the value of arr is equal to 2²³ that’s why we’re getting the error “RangeError: Invalid array length”. To overcome the error we can set the array length less than 2²³ and as an array should be a non-negative integer that’s why we’re getting the error for arr1
- When we extend an array by changing the length property than the number of actual element increases which causes the remain increased element to be a non-iterable empty slot.
let arr = [1, 2, 3, 4, 5, 6, 7]console.log(arr.length);
// 7arr.length = 9;
console.log(arr)
// [1, 2, 3, 4, 5, 6, 7, empty × 2]arr.forEach((elem => console.log(elem)));
// 1
// 2
// 3
// 4
// 5
// 6
// 7
map()
- It’s used to manipulate each and every array element of an array.
- The map object holds key-value pairs and remembers the original insertion order of the keys. ~MDN
- map() function is immutable (i.e; unalterable)
- Immutable refers to the objects whose state can’t be changed once the object is created.
let myFirstName = "Elon";let myFullName = myFirstName.concat("Musk")
In the above code, myFullName equals to Elon Musk and myFirstName equals to Elon states that once the string value is created, it can never change.
- No string methods change the string they operate on, they just return new strings. In fact, numbers, strings, and booleans all are immutable.
let mul = 5*7console.log(mul) //35
In the above example output is 35 but the initial values (i.e; 5 and 7) doesn’t change.
Why immutable code is better than the mutable code in javascript?
An object whose state can be changed once the object is created is a mutable object while in case of an immutable object, the state can’t be changed once the object is created.
Now let’s take an example:
let games = {
musicalChair :"indoor",
caromPool :"indoor",
cricket :"outdoor"
}// ----- IN CASE OF MUTABLE -----console.log(games)
// { musicalChair:"indoor", caromPool:"indoor", cricket:"outdoor" }// updating musical chair as outdoor game
games.musicalChair = "outdoor"console.log(games)
// { musicalChair:"outdoor", caromPool:"indoor", cricket:"outdoor" }// ----- IN CASE OF IMMUTABLE -----const {musicalChair, caromPool, cricket} = games
const updateGames = {
musicalChair : "outdoor"
}console.log(updateGames)
// {musicalChair: "outdoor"}
In the above code what I had done is rather than changing the object property I created a whole new object.
What’s the benefit?
- Immutability increases predictability
- Allows for mutation tracking
- Avoiding a reference clash
push() & pop()
- push() helps to add items to the end of an array and returns the new length of an array.
- pop() removes the last element of an array and returns that element.
- In the case of an empty array or if the length of an array is 0, pop() returns undefined.
let birds = ['Sparrow', 'Ostrich', 'Hawk', 'Parrot']// ----- IN CASE OF push() -----//adding new element to an array
let updateBirds = birds.push('Blackbird', 'Penguin')
console.log(updateBirds)
// 6console.log(birds)
//["Sparrow", "Ostrich", "Hawk", "Parrot", "Blackbird", "Penguin"]// ----- IN CASE OF pop() -----console.log(birds.pop());
// Parrot// in case of an empty array
let birds = []console.log(birds.pop());
// undefined
delete() & splice()
- delete() used to delete object properties.
- It won’t affect the length of an array.
let data = ["Sparrow", "cricket", "black", "kolkata"];// checking the length of data before deleting array element
console.log(data.length); //4// deleting the third element i.e; "kolkata"
console.log(delete data[3]); //trueconsole.log(data);
// [ 'Sparrow', 'cricket', 'black', <1 empty item> ]// checking the length of data after deleting array element
console.log(data.length); //4
So, the conclusion is even after deleting the element the length of the array is the same as before.
To overcome with this bug we can use splice()
let data = ["Sparrow", "cricket", "black", "kolkata"];
// to delete the element from the mentioned index value i.e; "3" to the number of elements should've deleted i.e; "1"
data.splice(3,1)
console.log(data)
//[ 'Sparrow', 'cricket', 'black' ]console.log(data.length)
// 3
filter()
- filter() method creates a new array with all elements that pass the test implemented by the provided function. ~MDN
- It’s immutable and introduced in ES6
- This method returns an array containing elements of the parent array that match the set test.
- It has a single parameter, a callback method which triggered as the filter method iterates through the array elements.
let arr = [12, 23, 34, 54, 76, 36, 89]console.log(arr.filter(elem => elem > 50))
//[ 54, 76, 89 ]console.log(arr.filter(elem => elem > 90))
// []
In the above example, I took a test function (i.e; “> 50”) which returns a new array containing the elements that matched the set test.
But in case of next test function (i.e; “> 90”) returns an empty array because of no matches.
shift() & unshift()
- shift() removes the element from the beginning of the array, returns the element that has been removed, update the indexes and the length property.
- unshift() add the element to the beginning of an array. It mutates the original array and returns the length of the original array after mutation.
let names = ['Bill', 'Gates', 'Warren', 'Buffett']console.log(names.length) //4// ----- IN CASE OF shift() -----console.log(names.shift());
// Bill// ----- IN CASE OF unshift() -----names.unshift('Elon', 'Musk');console.log(names);
// [ 'Elon', 'Musk', 'Bill', 'Gates', 'Warren', 'Buffett' ]console.log(names.length) //6
reduce()
- The reduce() method executes the reducer function on each element of the array, resulting in a single output value.
- It comes with some terminologies like reducer and accumulator.
- The reducer is what action we’ll perform in order to get one value.
- The accumulator accumulates callbacks return values.
Let’s take an array calc and add all the numbers existing in the array:
let calc = [2, -4, 6, 8]
let sum = 0;// ----- IN SIMPLE ALGO -----for(let elem of calc) {
sum += elem
}console.log(sum);
// 12// ----- USING reduce() METHOD -----sum = calc.reduce((accumulator, reducer) => {
return accumulator + reducer;
}, 0)console.log(sum);
// 12
So this conclusion is, by using reduce() method we can reduce all the elements of the array into a single value. Where the single value can be a number/string/object.
reduceRight()
- reduceRight() method reduces the array to a single value.
- The reduceRight() method applies a function against an accumulator and each value of the array (from right-to-left) to reduce it to a single value. ~MDN
- This method works in the same way as the reduce method, but in the opposite direction.
let arr = [ [ 3, 2, 6 ], [ 4, 8, 6 ], [ 9, 5 ] ]let newarr = arr.reduceRight((accumulator, reducer) => accumulator.concat(reducer));console.log(newarr);
// [ 9, 5, 4, 8, 6, 3, 2, 6 ]
NOTE: reduce() vs reduceRight()
reduce() method starts at the first element from left-to-right towards the last, whereas reduceRight() method starts at the first element from right-to-left towards the last.
let arr = ['1', '4', '6', '2', '9']let newArr1 = arr.reduce((accumulator, reducer) => accumulator + reducer)let newArr2 = arr.reduceRight((accumulator, reducer) => accumulator + reducer)console.log(newArr1);
//14629 (from left to right)console.log(newArr2);
//92641 (from right to left)