Reputation: 53
a = ['red', 'green', 'blue', 'purple']
a.each do |e|
puts e
if e == 'green'
a.delete(e)
end
end
By running the above code, I get the following output:
red
green
purple
Can someone explain to me what is happening behind the scene?
Upvotes: 0
Views: 805
Reputation: 52336
To add to Sergio's explanation, a more idiomatic approach to modifying the array would be:
a = ['red', 'green', 'blue', 'purple']
a.reject! do |e|
puts e
e == 'green'
end
so:
2.2.5 :001 > a = ['red', 'green', 'blue', 'purple']
=> ["red", "green", "blue", "purple"]
2.2.5 :002 > a.reject! do |e|
2.2.5 :003 > puts e
2.2.5 :004?> e == 'green'
2.2.5 :005?> end
red
green
blue
purple
=> ["red", "blue", "purple"]
2.2.5 :006 >
Upvotes: 3
Reputation: 230286
This is [part of] implementation of each
for (i=0; i<RARRAY_LEN(ary); i++) {
rb_yield(RARRAY_AREF(ary, i));
}
return ary;
So it simply moves a "reading head" along the array until it reaches the end.
When you delete "green"
, elements of the array are shifted to take its place and "blue"
is now where green was. But we already read the element at this location. Next element to be read is purple.
That is exactly why you should never mutate the collection you're iterating (unless this effect is what you actually desire).
I can't understand the native code
Here's a little visualization of that "reading head" mental model.
v
red green blue purple # print red
# next
v
red green blue purple # print green
v
red blue purple # delete green in the same iteration
# next
v
red blue purple # print purple
Upvotes: 7