I\'m using Ruby 1.9.2-p290 and found:
a = Array.new(2, []).each {|i| i.push(\"a\")}
=> [[\"a\", \"a\"], [\"a\", \"a\"]]
Which is not
This is a common misunderstanding. In your first example you are creating an array with 2 elements. Both of those are a pointer to the same array. So, when you iterate through your outer array you add 2 elements to the inner array, which is then reflected in your output twice
Compare these:
> array = Array.new(5, [])
=> [[], [], [], [], []]
# Note - 5 identical object IDs (memory locations)
> array.map { |o| o.object_id }
=> [70228709214620, 70228709214620, 70228709214620, 70228709214620, 70228709214620]
> array = Array.new(5) { [] }
=> [[], [], [], [], []]
# Note - 5 different object IDs (memory locations)
> array.map { |o| o.object_id }
=> [70228709185900, 70228709185880, 70228709185860, 70228709185840, 70228709185780]
From the ruby documentation:
new(size=0, obj=nil)
new(array)
new(size) {|index| block }
Returns a new array. In the first form, the new array is empty. In the second it is created with size copies of obj (that is, size references to the same obj). The third form creates a copy of the array passed as a parameter (the array is generated by calling to_ary on the parameter). In the last form, an array of the given size is created.
Thus, in the a
array you create, you have two references to the same array, thus the push
works on both of them. That is, you're pushing "a"
onto the same array twice. In the the b
array you create, you're actually creating a new array for each element.
In the first case you're using a single instance of an Array as a default for the elements of the main Array:
a = Array.new(2, []).each {|i| i.push("a")}
The second argument is simply recycled, so the push
is applied to the same instance twice. You've only created one instance here, the one being supplied as an argument, so it gets used over and over.
The second method is the correct way to do this:
b = Array.new(2) {Array.new}.each {|i| i.push("b")
This deliberately creates a new instance of an Array for each position in the main Array. The important difference here is the use of the block { ... }
which executes once for each position in the new Array. A short-form version of this would be:
b = Array.new(2) { [ ] }.each {|i| i.push("b")