0
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?

3
  • No, you should get "red, blue, purple". What's the ruby version you are using? Commented Sep 8, 2017 at 12:28
  • 2
    I am using ruby 2.3.3p222. Anyways I am deleting after puts, so 'green' should be printed any how. I am wondering how 'blue' is skipped? Commented Sep 8, 2017 at 12:30
  • Yes you are correct. Commented Sep 8, 2017 at 12:32

2 Answers 2

7

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
Sign up to request clarification or add additional context in comments.

2 Comments

The shift is made even before the block has completed its execution? Actually, I can't understand the native code :( Can you write a pseudocode?
@AkashCP The shift is made before delete returns, yes.
3

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 > 

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.