Strange behaviour with array of arrays


#1

Please observe the following code:

Create an array of arrays by explicitly stating values

test1 = [[nil, nil, nil], [nil, nil, nil], [nil, nil, nil]]
=> [[nil, nil, nil], [nil, nil, nil], [nil, nil, nil]]

Create an array of arrays using new method

test2 = Array.new(3, Array.new(3))
=> [[nil, nil, nil], [nil, nil, nil], [nil, nil, nil]]

#Both arrays appear to be equal
test1 == test2 => true

Modifying one of values behaves as expected

test1[1][1] = true => true
test1 => [[nil, nil, nil], [nil, true, nil], [nil, nil, nil]]

Modifying one of values behaves strangely

test2[1][1] = true => true
test2 => [[nil, true, nil], [nil, true, nil], [nil, true, nil]]

Can anyone explain why this is happening as I don’t understand why the
two array should behave differently? This is assuming this isn’t a
bug.


#2

On Jan 2, 2007, at 3:14 PM, Dan Stevens (IAmAI) wrote:

#Both arrays appear to be equal
Can anyone explain why this is happening as I don’t understand why the
two array should behave differently? This is assuming this isn’t a
bug.

Let me see if I can get Ruby to explain this for you:

a = [[nil, nil, nil], [nil, nil, nil], [nil, nil, nil]]
=> [[nil, nil, nil], [nil, nil, nil], [nil, nil, nil]]

a.map { |e| e.object_id }
=> [3797062, 3797052, 3797042]

a = Array.new(3, Array.new(3))
=> [[nil, nil, nil], [nil, nil, nil], [nil, nil, nil]]

a.map { |e| e.object_id }
=> [3779002, 3779002, 3779002]

a = Array.new(3) { Array.new(3) }
=> [[nil, nil, nil], [nil, nil, nil], [nil, nil, nil]]

a.map { |e| e.object_id }
=> [3760242, 3760232, 3760222]

As you can see, the new(n, obj) constructor uses the same object over
and over again. You want the block form (shown last) instead.

James Edward G. II


#3

On 1/2/07, Dan Stevens (IAmAI) removed_email_address@domain.invalid wrote:

#Both arrays appear to be equal
Can anyone explain why this is happening as I don’t understand why the
two array should behave differently? This is assuming this isn’t a
bug.

The syntax you want is this …

test2 = Array.new(3) {Array.new(3)}

This will create a new array of three elements and then create a a new
array object for each element of test2

test2 = Array.new(3, Array.new(3))

This will create a new array of three elements but use the same object
for element of test2

Blessings,
TwP