Ruby 2D array assignment gotcha

1 minute read

Recently I came across an interesting ruby Array class gotcha.

As usual, I will start with the example.

Let us instantiate a 2D array.

arr = Array.new(3, Array.new(3, 0))

Now, we can assign a value to a cell in the matrix

arr[1][1] = 1
puts arr
# [[0, 1, 0], [0, 1, 0], [0, 1, 0]]

Huh! (scratches head)

Let us try without using Array.new

arr_2 = []
3.times { arr_2 << Array.new(3, 0)}
arr_2[1][1] = 1
puts arr_2
# [[0, 0, 0], [0, 1, 0], [0, 0, 0]]

This looks ok.

Time to hit the docs

Note that the second argument populates the array with references to the same object. Therefore, it is only recommended in cases when you need to instantiate arrays with natively immutable objects such as Symbols, numbers, true or false.

To declutter our initial arr instantiation, the row was initialized only once and used multiple times.

row = Array.new(3, 0)
arr = Array.new(3, row)

We can check some more

arr = Array.new(3, Array.new(3, 0))
arr[0].equal? arr[1]   # will return true
arr[0].equal? arr[2]   # will return true

arr_2 = []
3.times { arr_2 << Array.new(3, 0)}
arr_2[0].equal? arr_2[1]   # will return false
arr_2[0].equal? arr_2[2]   # will return false

There is a safer alternative available. We can use blocks

arr = Array.new(3) { Array.new(3, 0) }
arr[0].equal? arr[1]   # will return false
arr[0].equal? arr[2]   # will return false

arr[1][1] = 1
puts arr
# [[0, 0, 0], [0, 1, 0], [0, 0, 0]]

Comments