Ruby didn't loop all the elements?

I am trying to remove everything 7from an array:

a = [1,2,3,4,5,7,7,7,7,7,7,7,7,7,9,10,11,12,13]

I did:

a.each_with_index do |item, index|
  if item == 7
    a.delete_at(index)
  end
end
a # => [1, 2, 3, 4, 5, 7, 7, 7, 7, 9, 10, 11, 12, 13]  

How did this happen?

+4
source share
3 answers

The fact that only half (5/9) of your items has disappeared is a dead sale, that the problem is being removed, iterating through the collection.

The iteration will process indices 1, 2, 3, 4, etc. If you delete index 2 while it is being processed, this will shift all subsequent indexes by one.

So, when you go to index 3 in the next iteration, you will skip the original index 3, because it will be shifted down to index 2.

In other words, let's start with a simpler example with two consecutive items to delete:

index | 0 | 1 | 2 | 3 |
value | 1 | 7 | 7 | 9 |

, 1, . 7, , :

index | 0 | 1 | 2 |
value | 1 | 7 | 9 |

, 9, . , .

, , , , . , Ruby. .

, , , ( ) . 5/9 7 , .

( Ruby) - :

a.delete(7)

, , 7:

a.delete_if {|val| val > 7}

( ), , , - , , .

, . , :

a.to_enum.with_index.reverse_each do |item, index|

, . , , , , .

- , delete delete_if - , Ruby, , .

+7

, / , .

delete:

a = [1,2,3,4,5,7,7,7,7,7,7,7,7,7,9,10,11,12,13]
a.delete(7)
# [1, 2, 3, 4, 5, 9, 10, 11, 12, 13]

, :

a = [1,2,3,4,5,6]
a.each_with_index do |item, index|
    puts "#{index} : #{item}"
    if item == 4
        a.delete_at(index)
    end
end

:

0 : 1
1 : 2
2 : 3
3 : 4  # made delete here
4 : 6  # See the problem !

, :)

+3

, , , , . , , Array#each_index.

VALUE rb_ary_each_index(VALUE ary)
{
    long i;
    RETURN_SIZED_ENUMERATOR(ary, 0, 0, ary_enum_length);

    for (i=0; i<RARRAY_LEN(ary); i++) {
        rb_yield(LONG2NUM(i));
    }
    return ary;
}

, for 0 . 0 .

When you remove an element from the array, everything after its offset 1. If i = 5you call a.delete_at(i), which means a[6]now a[5]. a[7]now a[6]. Etc. The next iteration will have i = 6, which means that you actually missed the item.

To illustrate and suggest that you want to remove 2.

i = 0
a = [1,2,2,2,3,4,5]
     ^
-------------------
i = 1
a = [1,2,2,2,3,4,5]
       ^
a.delete_at(i)
a = [1,2,2,3,4,5]
-------------------
i = 2
a = [1,2,2,3,4,5]
         ^
a.delete_at(i)
a = [1,2,3,4,5]
-------------------
i = 3
a = [1,2,3,4,5]
           ^
-------------------
i = 4
a = [1,2,3,4,5]
             ^
-------------------
i = 5
a = [1,2,3,4,5]
-------------------
i = 6
a = [1,2,3,4,5]

Note that the last two iterations left the array, because the array is now two elements smaller than before.

+2
source

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


All Articles