Today I found a little counterintuitive oddity in Ruby. Take the following code example:
a = [0,1,2,3,4,5,6,7,8,9,10]
h = {0=>0, 1=>1, 2=>2, 3=>3, 4=>4, 5=>5,
6=>6, 7=>7, 8=>8, 9=>9, 10=>10}
puts "Iterating & deleting array"
a.each do |i|
puts "On #{i}"
if i % 3 == 0
puts "Deleting #{i}"
a.delete(i)
end
end
puts "Iterating & deleting hash"
h.each do |k,v|
puts "On #{v}"
if v % 3 == 0
puts "Deleting #{v}"
h.delete(k)
end
end
With the output:
Iterating & deleting array
On 0
Deleting 0
On 2
On 3
Deleting 3
On 5
On 6
Deleting 6
On 8
On 9
Deleting 9
Iterating & deleting hash
On 5
On 0
Deleting 0
On 6
Deleting 6
On 1
On 7
On 2
On 8
On 3
Deleting 3
On 9
Deleting 9
On 4
On 10
Notice how each time the array deletes an item, it happens to skip and never evaluate the next item? Ruby internally uses an index counter (like if you were manually iterating the array with a for loop). When an item is deleted everything shifts back one.
With the Hash (it’s a little hard to see because they’re out of order) no items get skipped.
I’m not saying this is a bug or is necessarily wrong, but a Hash and an Array are very similar data structures (especially insofar as they are both enumerable) and I’m surprised that they work differently. I also spent a LONG time and a LOT of debugging to figure out that this was the error (it was buried deep in some XHR Rails calls, so reproducing it took a lot of time)
I’d love to hear from anyone who can attest to what happens in other language iterators (Java, Python, etc)!