steel
steel

Reputation: 12520

Simple Ruby loop through array does not iterate completely

So I thought I understood this, but I'm not getting the output I expected, so obviously I don't understand it.

In Ruby (2.0.0)

a = [1,2,3,4]
a.each do |e|
    a.delete(e)
end
a = [2,4]

It doesn't seem to be looping through each item in the array. However, when I simply output the item, it loops through each item. There's some mechanism of a.delete(e) that is affecting the iteration.

a = [1,2,3,4]
a.each do |e|
    puts e
end
=> 1
=> 2
=> 3
=> 4

Ultimately, I want to put a conditional into the loop, such as:

a = [1,2,3,4]
a.each do |e|
    if e < 3
        a.delete(e)
    end
end

How can I get this loop it iterate through each item and delete it? Thank you!

Upvotes: 1

Views: 4373

Answers (2)

sawa
sawa

Reputation: 168081

With

a = [1,2,3,4]
a.each do |e|
  a.delete(e)
end
a # => [2, 4]

The first iteration was at index 0 with e being 1. That being deleted, a becomes [2,3,4] and the next iteration is at index 1, with e being 3. That being deleted, a becomes [2,4]. The next iteration would be at index 2, but since a is not that long anymore, it stops, returning a's value as [2, 4].

In order to iterate through each item and delete it, given that there is no duplicate, a common way is to iterate backwards.

a = [1,2,3,4]
a.reverse_each do |e|
  a.delete(e)
end
a # => []

a = [1,2,3,4]
a.reverse_each do |e|
  if e < 3
    a.delete(e)
  end
end
a # => [3, 4]

Upvotes: 4

Arie Xiao
Arie Xiao

Reputation: 14082

DO NOT mutate a collection when you iterate over it, unless you know what you are doing.

For your ultimate purpose,

a = [1,2,3,4]
a.reject!{|e| e < 3 }

Upvotes: 6

Related Questions