i know this is an annoying question, but can someone explain me why splice method is executing in a weird way. Please explain me why the expected output is different from the actual result.
let numbers = [15, 12, 15, 3, 5, 4, 6];
// Get the indexes of the numbers greater than 5
let indexes = numbers.reduce((arr, current, index) => {
if (current > 5) {
arr.push(index);
}
return arr;
}, []);
// Loop through the indexes while removing the indexes from the numbers array
indexes.forEach((element) => {
numbers.splice(element, 1);
});
// expected result: numbers = [ 3 , 5, 4 ];
// actual result: numbers = [ 12, 3, 4, 6 ]
3 Answers 3
.splice() changes the array it is used on. You might have already known this, but if you debug your code using a console.log, you'll see what's happening; in short, your first number > 5 is 15. 15 is at index 0, so you remove index 0. However, as splice changes the array it is used on, 12 becomes index 0, and then the second 15 index 1, and so on and so forth. So for example, your code has the following indexes: 0, 1, 2, 6.
- The first time you remove index 0:
[12, 15, 3, 5, 4, 6] - Then you remove index 1:
[12, 3, 5, 4, 6] - Then you remove index 2:
[12, 3, 4, 6] - Then you remove index 6, which doesn't exist:
[12, 3, 4, 6]
The better way of accomplishing that goal is with .filter(). Filter creates a new array of all items that pass the test given in the callback, so:
numbers = numbers.filter((num) => num < 6);
That's the arrow function expression shorthand to return only numbers less than 6.
Comments
splice actually removes the item in place. It does not create any copy of array. In your case after reduce operation, indexes would be
[0, 1, 2, 6]
and then while iterating and splicing, in first iteration array with position 0 is removed so array becomes
numbers = [12, 15, 3, 5, 4, 6];
and its length is also reduced. On next iteration of forEach array element with index position 1 is removed which is 15 in our case. So after second iteration array becomes
numbers = [12, 3, 5, 4, 6];
Similarly in next subsequent iteration you will have result like
[12, 3, 4, 6]
Comments
As someone has mentioned the problem is with applying changes over an array that is mutated in every iteration.
I assume the example is for learning purposes as it would have been easier to write it like:
let numbers = [15, 12, 15, 3, 5, 4, 6]
numbers.filter(elem => elem <= 5)
In any case, and following the demonstration code, it would be good to stress the dangerous of mutations that is prone to spooky effects. I have rewritten the code in a more functional style:
let numbers = [15, 12, 15, 3, 5, 4, 6];
// Get the indexes of the numbers greater than 5
let indexes = numbers.reduce((arr, current, index) => {
if (current > 5) {
return arr.concat(index);
}
return arr;
}, []);
// Instead of removing we create a new array filtering out the elements we dont want
let filteredNumbers = numbers.filter((_,index) => indexes.indexOf(index) === -1)
console.log(filteredNumbers)
// expected result: numbers = [ 3 , 5, 4 ];
// actual result: numbers = [ 3, 5, 4 ]
numbers.filter(item => item < 6)