Delete array element during iteration

I iterate over a nested array with two blocks each and removing an element from the same array inside the internal iteration:

 arr = [1,2,3] arr.each do |x| arr.each do |y| puts "#{arr.delete(y)}" end end 

This leads to results 1 , 3 . The array becomes [2] .

Why is the value 2 not passed to the first or second cycle? Is this some kind of side effect of nested iteration?

+5
source share
4 answers

This is due to the index of the deleted item. I added some results 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 item is 1 (index is 0) in the indoor unit. After deleting, 2 has index 0, and now each iteration goes to index 1, which is now element 3. 3 will be deleted and that is the end of the iteration. So, you get [2].

The same thing happens without every nesting:

 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 using reverse_each for such operations to avoid this:

 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 => [] 
+7
source

This has nothing to do with the nest. In fact, you will get the same result only with an inner loop:

 arr = [1,2,3] arr.each do |y| puts "#{arr.delete(y)}" end # => outputs 1, 3 a # => [2] 

Compilation involves changing the array during iteration.


The reason is that Array#each is index based. First, x becomes 1 (which is completely irrelevant to the result). In the inner loop you first have:

  • a : [1, 2, 3] , index : 0 , y : 1

where index is the index on which the internal iteration is based, and you remove y and you get:

  • a : [2, 3]

In the following internal iteration, you have:

  • a : [2, 3] , index : 1 , y : 3

Note that 2 skipped since the iteration is based on index ( 1 ). Then 3 is removed, which gives:

  • a : [2] .

When the outer loop tries to perform the next iteration at index 1 , there are fewer elements left at a , so it ends there.

+2
source

To understand this case, we take the case of a simple array passing with an index.

You have an array with [1,2,3] .

When you start iteration with 0, the current element is 1. Now you delete element 1 with index 0, your array will become [2,3] .

In the next iteration, your index will be 1, and it will point to 3. And 3 will be deleted. Your array will be [2] .

Now your index is 2 and the array is 1. So nothing will happen. Now that this inner loop is completed, the outer loop will resume with the updated index 1, and then to 2. And since the array is 1, they will not be executed.

So, by going this, it seems to be using the index as an iteration.

As I know, it should have undefined behavior (for example, in C ++ such code is not recommended). Since when iterating again, if you delete the current element, this will damage the pointer value (currently held in the function block parameter passed to each ).

+1
source

since each iterates with the indices, and you delete the element in the inner loop every other element of the next iteration. if you increase the number of elements and include the current iteration index in the loop, you will see a large picture.

 arr = [1,2,3,4,5,6,7,8,9] arr.each_with_index do |x,ix| puts "loop1: #{arr.inspect}, x: #{x}, ix: #{ix}" arr.each_with_index do |y, iy| puts "loop2: #{arr.inspect}, y: #{y}, iy: #{iy}" puts "#{arr.delete(y)}" end end 

result

 loop1: [1, 2, 3, 4, 5, 6, 7, 8, 9], x: 1, ix: 0 loop2: [1, 2, 3, 4, 5, 6, 7, 8, 9], y: 1, iy: 0 1 loop2: [2, 3, 4, 5, 6, 7, 8, 9], y: 3, iy: 1 3 loop2: [2, 4, 5, 6, 7, 8, 9], y: 5, iy: 2 5 loop2: [2, 4, 6, 7, 8, 9], y: 7, iy: 3 7 loop2: [2, 4, 6, 8, 9], y: 9, iy: 4 9 loop1: [2, 4, 6, 8], x: 4, ix: 1 loop2: [2, 4, 6, 8], y: 2, iy: 0 2 loop2: [4, 6, 8], y: 6, iy: 1 6 => [4, 8] 

since you delete during the loop, and after each iteration, the index increases, but the array is one of the elements, so it removes the next (and all) corresponding element (s), available, and at the end the loop compares and stops the loop when index >= length

0
source

Source: https://habr.com/ru/post/1243372/


All Articles