NamNamNam NamNamNam - 4 months ago 9
Ruby Question

Ruby didn't loop all element?

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

a.each_with_index do |item, index|
if item == 7
a.delete_at(index)
end
end
print a


I am trying to remove all 7 in array

But the result always [1, 2, 3, 4, 5, 7, 7, 7, 7, 9, 10, 11, 12, 13]

I am like.. what??!

How this happen?

ruby -v


ruby 2.3.0p0 (2015-12-25 revision 53290) [x86_64-linux]


Ubuntu 16.04

Answer

The fact that only about half (5/9) of your 7s have disappeared is a dead giveaway that the problem is deleting while iterating over the collection.

The iteration will be processing indexes 1, 2, 3, 4 and so on so, if you delete index 2, that will shift all later indexes down by one. So, when you then move to index 3, you will skip the original index 3 because that will have been shifted down to index 2.

In other words, let's start with a simpler example:

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

You check the first index and the value is 1 so you do nothing. You then check the second index and the value is 7 so you delete it, giving:

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

You then check the third index and the value is 9 so you do nothing. You've also reached the end so it stops.

So you can see there that you actually skipped the second 7 because you shifted things around while iterating. This isn't a Ruby-specific problem, a great many languages have this same issue.

The correct way (in Ruby) to delete all items of a single given value is to use the array delete method:

a.delete(7)

You can also use conditional delete for more complex conditions such as deleting everything greater than 7:

a.delete_if {|val| val > 7}