Lokesh Lokesh - 2 months ago 6
Ruby Question

Ruby hash bug, anomality or normal?

Is the following, a Ruby bug or perfectly normal?

> a = [1, 2, 3].zip([[]]*3).to_h
=> {1=>[], 2=>[], 3=>[]}
> a[1] << 2
=> [2]
> a
=> {1=>[2], 2=>[2], 3=>[2]}


I do not expect all values to get appended by
2
as I only meant to add
2
in the place where key is
1
.

Answer

It is expected, since when you're creating the second array in zip is the same object:

a = [1, 2, 3].zip([[]]*3)
 #=> [[1, []], [2, []], [3, []]]
a.map{|aa| aa.last.object_id }
 #=> [70307853627580, 70307853627580, 70307853627580]

So, when you try to update that object, it gets reflected across the object a:

a[0][1] << 2
 #=> [2]
p a
 #=> [[1, [2]], [2, [2]], [3, [2]]]

To create an array with separate objects a block can be passed instead. This method is safe to use with mutable objects such as hashes, strings or other arrays:

a = [1, 2, 3].zip( Array.new(3) { [] } )
 #=> [[1, []], [2, []], [3, []]]
a[0][1] << 2
 #=> [2]
p a
 #=> [[1, [2]], [2, []], [3, []]]

I would encourage you to read this article which talks about such scenarios with hashes by Aaron Patterson.