While on the nodejs REPL I was trying to clean up an array defined as const array = [...]
and just found out that using array.forEach(() => /pop|shift/())
would not work. After such expression the array will still hold values in it.
I'm well aware of better methods to clean the array, like array.splice(0)
, but I'm really curious about this behavior as seems counter-intuitive, at least for me.
Here's the test:
const a = [1, 2, 3]
a.forEach(() => {
a.shift()
})
console.log(a) // [ 3 ]
const b = [1, 2, 3]
b.forEach(() => {
b.pop()
})
console.log(b) // prints [ 1 ]
While on the nodejs REPL I was trying to clean up an array defined as const array = [...]
and just found out that using array.forEach(() => /pop|shift/())
would not work. After such expression the array will still hold values in it.
I'm well aware of better methods to clean the array, like array.splice(0)
, but I'm really curious about this behavior as seems counter-intuitive, at least for me.
Here's the test:
const a = [1, 2, 3]
a.forEach(() => {
a.shift()
})
console.log(a) // [ 3 ]
const b = [1, 2, 3]
b.forEach(() => {
b.pop()
})
console.log(b) // prints [ 1 ]
Notes
At first I was using
arr.forEach(() => arr.pop())
, so I though that one of the values was short-circuiting theforEach
but wrapping the lambda in a body-block{ .. }
will also produce the same results.The results are consistent across different node versions and browsers .. so it seems like it's well-defined behavior.
The quantity of leftover values, those still in the result array, change depending on the length of the input array, and seems to be
Math.floor(array.length / 2)
The leftover values are always ordered accordingly to the
/pop|shift/
method used, so some of the calls are actually changing the input array.It also returns the same results by calling
Array.prototype.forEach(array, fn)
- 5 You're iterating from the beginning and removing the last element each iteration. This means you're moving forward 1, and reducing the length by 1, each iteration. Hence why you're ending up with floor(initialLength / 2) iterations. – John Commented Jan 7, 2019 at 21:10
-
My output on Chrome 71 is a bit different:
[5, 6, 7]
for the first log and[1, 2, 3]
for the second – Matthew Herbst Commented Jan 7, 2019 at 21:12 - @MatthewHerbst I fixed the input arrays! – eridal Commented Jan 7, 2019 at 21:14
-
1
Use
array.length = 0;
to empty an array. – Bergi Commented Jan 7, 2019 at 21:21
3 Answers
Reset to default 4Check out this quote from here: https://developer.mozilla/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach
If the values of existing elements of the array are changed, the value passed to callback will be the value at the time forEach() visits them; elements that are deleted before being visited are not visited.
You're iterating from the beginning and removing the last element each iteration. This means you're moving forward 1, and reducing the length by 1, each iteration. Hence why you're ending up with floor(initialLength / 2)
iterations. You're modifying the same array that you're forEach
ing, which as stated above means that you will not have the callback invoked for those pop
'd elements.
Modifying an array while iterating over it is generally a bad idea. In fact, in Java, trying to do so would cause an exception to be thrown. But let's convert the forEach
into an old-school for loop, and maybe you'll see the issue.
for (let i = 0; i < a.length; ++i) {
a.pop();
}
Is it clearer now what's going on? Each iteration you're shortening the length of the array by 1 when you pop the last element off. So the loop will end after iterating over half the elements -- because by then, it will have REMOVED half the elements, too, causing the value of i
to be more than the current length of the array.
The same thing is happening when you use forEach
: you're shortening the array with each iteration when you pop, causing the loop to terminate after only half the elements have been iterated. In other words, the iterator variable will move forward past the end of the array as the array shrinks.
.pop
Let's do this instead:
let arr = 'abcde'.split('');
arr.forEach((x,i,a) => {
console.log('before', x,i,a);
arr.pop();
console.log('after', x,i,a);
});
console.log(arr);
Your index is incrementing but your length is decrementing, so you're deleting the last elements when your index is in the first elements, thus the result where you delete the right half of the array.
.shift
Same: the iterating index goes one way, the length in another, so the whole things stop mid-work:
let arr = 'abcde'.split('');
arr.forEach((x,i,a) => {
console.log('before', x,i,a);
arr.shift();
console.log('after', x,i,a);
});
console.log(arr);
发布者:admin,转转请注明出处:http://www.yc00.com/questions/1744708022a4589171.html
评论列表(0条)