2

I've noticed an strange behavior when trying to fill an array of arrays with a loop variable.

N = 5
M = 4 

m = Array.new(N, Array.new(M, -1))
for j in 0..N-1
  m[j][0] = j
end

m

Doing this will end up with:

4 -1 -1 -1
4 -1 -1 -1
4 -1 -1 -1
4 -1 -1 -1
4 -1 -1 -1

But I expected:

0 -1 -1 -1
1 -1 -1 -1
2 -1 -1 -1
3 -1 -1 -1
4 -1 -1 -1

In the other hand, this works as expected:

N = 5

m = Array.new(N)
for i in 0..N-1
  m[i] = i
end

puts m  # => [0, 1, 2, 3, 4]

What am I missing here?

2 Answers 2

9

m = Array.new(N, Array.new(M, -1)) creates a size N array, each of whose members is the same object - a size M array filled with -1. So when you set the first element of the inner array, the change is relected in each of the outer arrays, because they all contain the same object.

Instead, use the block form:

Array.new(N) { Array.new(M, -1) }

The block is called for each element of the outer array, causing each one to get a different new inner array.

Sign up to request clarification or add additional context in comments.

Comments

1

Just do:

 Array.new(N) { Array.new(M, -1) }

instead of:

Array.new(N, Array.new(M, -1))

Read the documentation:

Case 1:

new(size) {|index| block }

an array of the given size is created. Each element in this array is created by passing the element’s index to the given block and storing the return value.

Example:

ary_new_without_block = Array.new(3) { [] }
ary_new_without_block.map(&:object_id) # => [75888070, 75888060, 75888050]

Look at the object ids, all are different, which ensures that all the inner array objects are different. Thus if you change one any inner array object, changes will not be seen in all element array(s):

ary_new_without_block[0] << 2
ary_new_without_block # => [[2], [], []]

Case 2:

new(array)

creates a copy of the array passed as a parameter (the array is generated by calling #to_ary on the parameter).

Example:

ary_new_block = Array.new(3,[])
ary_new_block.map(&:object_id) # => [75888980, 75888980, 75888980]

Look at the object ids, all are same, which ensures that all the inner array objects are not different. Thus if you change one inner array object, changes will be seen in all elements:

ary_new_block[0] << 2
ary_new_block # => [[2], [2], [2]]

You choose the second case, thus you didn't get the output as you were looking for. But the first case will help you to reach to your goal.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.