I am iterating over a nested array with two each
blocks, and deleting an element from the same array inside the inner iteration:
arr = [1,2,3]
arr.each do |x|
arr.each do |y|
puts "#{arr.delete(y)}"
end
end
This produces the results 1
, 3
. The array becomes [2]
.
Why isn't the value 2
passed to the first or the second loop? Is this some sort of side effect of nested iteration?
Use unset() function to remove array elements in a foreach loop. The unset() function is an inbuilt function in PHP which is used to unset a specified variable. The behavior of this function depends on different things.
In Java 8, we can use the Collection#removeIf API to remove items from a List while iterating it.
Java arrays do not provide a direct remove method to remove an element. In fact, we have already discussed that arrays in Java are static so the size of the arrays cannot change once they are instantiated. Thus we cannot delete an element and reduce the array size.
For Loop to Traverse Arrays. We can use iteration with a for loop to visit each element of an array. This is called traversing the array. Just start the index at 0 and loop while the index is less than the length of the array.
It's because of the index of the deleted element. I added some output to show you:
arr = [1,2,3]
arr.each do |x|
puts "1: #{arr.inspect}, x: #{x}"
arr.each do |y|
puts "2: #{arr.inspect}, y: #{y}"
puts "#{arr.delete(y)}"
end
end
Result:
1: [1, 2, 3], x: 1
2: [1, 2, 3], y: 1
1
2: [2, 3], y: 3
3
=> [2]
The first deleted element is 1 (index is 0) in the inner each block. After the deletion 2 has the index 0 and now the each iteration goes to index 1 which is now element 3. 3 will be deleted and that's the end of the iteration. So you get [2].
The same happens without nested each:
arr = [1,2,3]
arr.each do |x|
puts "1: #{arr.inspect}, x: #{x}"
puts "#{arr.delete(x)}"
end
Result:
1: [1, 2, 3], x: 1
1
1: [2, 3], x: 3
3
=> [2]
I suggest to use reverse_each
for such operations to avoid this behavior:
arr = [1,2,3]
arr.reverse_each do |x|
puts "1: #{arr.inspect}, x: #{x}"
puts "#{arr.delete(x)}"
end
Result:
1: [1, 2, 3], x: 3
3
1: [1, 2], x: 2
2
1: [1], x: 1
1
=> []
It has nothing to do with nesting. In fact, you will get the same result with just the inner loop:
arr = [1,2,3]
arr.each do |y|
puts "#{arr.delete(y)}"
end
# => outputs 1, 3
a # => [2]
The compication is due to modifying the array during iteration.
The reason is because Array#each
is based on the index. First, x
becomes 1
(which is entirely irrelevant to the result). Within the inner loop, first you have:
a
: [1, 2, 3]
, index
: 0
, y
: 1
where index
is the index on which the inner iteration is based, and you delete y
and you get:
a
: [2, 3]
In the next inner iteration, you have:
a
: [2, 3]
, index
: 1
, y
: 3
Note that 2
is skipped because iteration is based on the index (1
). Then, 3
is deleted, which gives:
a
: [2]
.When the outer loop tries the next iteration at index 1
, there is not enough elements left in a
, so it ends there.
To understand this case, let us take case of a simple array being traversed with index.
You have an array with [1,2,3]
.
When you start iteration with 0, current element is 1. Now, you delete the element 1 at index 0, your array will become [2,3]
.
In the next iteration, your index will be 1 and that will point to 3. And 3 will be deleted. Your array will be [2]
.
Now, your index is 2 and array has length 1. So, nothing will happen. Now, when this inner loop will complete, outer loop will resume at updated index 1 and then to 2. And as array has length of 1, they will not be executed.
So, going by this, it seems that is using index as iteration.
As per my knowledge, it should have undefined behaviour (like in C++ such code is not recommended). Because, while iterating if you delete the current element, it will corrupt the pointer value (currently being held in the parameter of function block passed to each
).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With