I have an array of arrays and want to append elements to the sub-arrays. += does what I want, but I'd like to understand why push does not.
Behavior I expect (and works with +=):
b = Array.new(3,[])
b[0] += ["apple"]
b[1] += ["orange"]
b[2] += ["frog"]
b => [["apple"], ["orange"], ["frog"]]
With push I get the pushed element appended to EACH sub-array (why?):
a = Array.new(3,[])
a[0].push("apple")
a[1].push("orange")
a[2].push("frog")
a => [["apple", "orange", "frog"], ["apple", "orange", "frog"], ["apple", "orange", "frog"]]
Any help on this much appreciated.
The issue here is b = Array.new(3, [])
uses the same object as the base value for all the array cells:
b = Array.new(3, [])
b[0].object_id #=> 28424380
b[1].object_id #=> 28424380
b[2].object_id #=> 28424380
So when you use b[0].push
, it adds the item to "each" sub-array because they are all, in fact, the same array.
So why does b[0] += ["value"] work? Well, looking at the ruby docs:
ary + other_ary → new_ary
Concatenation — Returns a new array built by concatenating the two arrays together to produce a third array.
[ 1, 2, 3 ] + [ 4, 5 ] #=> [ 1, 2, 3, 4, 5 ]
a = [ "a", "b", "c" ]
c = a + [ "d", "e", "f" ]
c #=> [ "a", "b", "c", "d", "e", "f" ]
a #=> [ "a", "b", "c" ]
Note that
x += y
is the same as
x = x + y
This means that it produces a new array. As a consequence, repeated use of += on arrays can be quite inefficient.
So when you use +=
, it replaces the array entirely, meaning the array in b[0]
is no longer the same as b[1]
or b[2]
.
As you can see:
b = Array.new(3, [])
b[0].push("test")
b #=> [["test"], ["test"], ["test"]]
b[0].object_id #=> 28424380
b[1].object_id #=> 28424380
b[2].object_id #=> 28424380
b[0] += ["foo"]
b #=> [["test", "foo"], ["test"], ["test"]]
b[0].object_id #=> 38275912
b[1].object_id #=> 28424380
b[2].object_id #=> 28424380
If you're wondering how to ensure each array is unique when initializing an array of arrays, you can do so like this:
b = Array.new(3) { [] }
This different syntax lets you pass a block of code which gets run for each cell to calculate its original value. Since the block is run for each cell, a separate array is created each time.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With