New Array behavior in Ruby
1. Surprise
Array.new(5, [])
returns a new array with 5 []
elements, but those elements are actually references to the same object:
irb> a = Array.new(5, [])
#=> [[], [], [], [], []]
irb> a.first << ?c
#=> ["c"]
irb> a
#=> [["c"], ["c"], ["c"], ["c"], ["c"]]
This is confirmed by looking at the underlying ids:
irb> a.map(&:object_id)
#=> [69987008691280,
# 69987008691280,
# 69987008691280,
# 69987008691280,
# 69987008691280]
irb> a.map(&:object_id).uniq!
#=> [69987008691280]
2. Solution
The correct way of generating a list of distinct object is to pass []
to a block. The block is evaluated for each new element in the array:
irb> a = Array.new(5) { [] }
#=> [[], [], [], [], []]
irb> a.first << ?c
#=> ["c"]
irb> a
#=> [["c"], [], [], [], []]
irb> a.map(&:object_id)
#=> [69987008612720,
# 69987008612700,
# 69987008612680,
# 69987008612660,
# 69987008612640]
irb> a.map(&:object_id).uniq!
#=> nil
3. How it works
[]
was returning a new object on each call:
irb> b = []
#=> []
irb> a = Array.new(5) { b }
#=> [[], [], [], [], []]
irb(main):010:0> a.first << ?c
#=> ["c"]
irb> a
#=> [["c"], ["c"], ["c"], ["c"], ["c"]]
irb> a.map(&:object_id)
#=> [69987008484780,
# 69987008484780,
# 69987008484780,
# 69987008484780,
# 69987008484780]
irb> a.map(&:object_id).uniq!
#=> [69987008484780]