3x3 Array of int, changing one value affects others

ruby-1.9.2-head > a = Array.new(3, Array.new(3, 0))
=> [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
ruby-1.9.2-head > a[0][2] = 1
=> 1
ruby-1.9.2-head > a
=> [[0, 0, 1], [0, 0, 1], [0, 0, 1]]

If the values aren't all 0s, a[0][2] behaves correctly. Am I doing
something wrong? Thanks.

···

--
Posted via http://www.ruby-forum.com/.

That constructor of Array takes the second argument, and places it
(the same object) in all positions of the array.
So when you modify that object, you see the modification in all
positions. If you want a different object in each position, try the
block form:

irb(main):001:0> a = Array.new(3,Array.new(3,0))
=> [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
irb(main):002:0> a[0].object_id
=> -609376368
irb(main):003:0> a[1].object_id
=> -609376368
irb(main):004:0> a[2].object_id
=> -609376368

# Same object id

irb(main):005:0> a = Array.new(3) {Array.new(3,0)}
=> [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
irb(main):006:0> a[0].object_id
=> -609423628
irb(main):007:0> a[1].object_id
=> -609423638
irb(main):010:0> a[2].object_id
=> -609423678

# different object id

Jesus.

···

On Tue, Sep 28, 2010 at 10:47 AM, Williams Williams <shaolinog@hotmail.com> wrote:

ruby-1.9.2-head > a = Array.new(3, Array.new(3, 0))
=> [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
ruby-1.9.2-head > a[0][2] = 1
=> 1
ruby-1.9.2-head > a
=> [[0, 0, 1], [0, 0, 1], [0, 0, 1]]

If the values aren't all 0s, a[0][2] behaves correctly. Am I doing
something wrong? Thanks.

Array.new(size, obj) doesn't work the way you think. It creates an array with
size elements, where each element is the *same object*. By same object, I
don't mean different objects equal to each other, but one single object (you
can see this looking at the object_id of the sub-arrays). So, it should be no
wonder that modifying one of the elements also changes the others. If you want
to create an array containing three different arrays [0,0,0], you need to use
the block form of Array.new:

Array.new(3){|i| Array.new(3,0)}

This constructor creates an array of the given size, then calls the block for
each index of the array and stores in that index the value returned by the
block. Different from your code, now the array contains three arrays with
different identities, all with the same contents. This is because every time
the block is called, it returns a new array.

To be more clear, your code has the same results as:

# create an array of size 3 filled with nil
a = Array.new(3)
# create the subarray
subarray = Array.new(3,0)
#store the same subarray in all the three entries of the array
a[0] = subarray
a[1] = subarray
a[2] = subarray

The code which works as you want, instead, produces the same as:
# create an array of size 3 filled with nil
a = Array.new(3)
#store in the tree entries of the array three different arrays with
#the same contents
a[0] = Array.new(3,0)
a[1] = Array.new(3,0)
a[2] = Array.new(3,0)

Usually, you only use Array.new(size, obj) when obj is an immutable object,
that is an object with no methods which modify it. Your use of Array.new(3,0),
for instance, is correct because an Integer can't be modified. The same would
be true, for example, for symbols. If obj is a mutable object, like a string,
an array or a hash (which all have methods which change the object itself),
creating an array this way can lead to unexpected result, as you have
experienced yourself.

I hope this helps

Stefano

···

On Tuesday 28 September 2010, Williams Williams wrote:

>ruby-1.9.2-head > a = Array.new(3, Array.new(3, 0))
> => [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
>ruby-1.9.2-head > a[0][2] = 1
> => 1
>ruby-1.9.2-head > a
> => [[0, 0, 1], [0, 0, 1], [0, 0, 1]]
>
>If the values aren't all 0s, a[0][2] behaves correctly. Am I doing
>something wrong? Thanks.