Weird behavior pushing an array onto an array

Greetings,

In my program, I fill an array, a, with data and push it onto an array b
which is initially empty. At each iteration, all the previous pushed
values become the same as the last item, so I end up with an array of
many copies of the last item.

Can you please help?

Joe.

(rdb:1) c
ACFFX 25.01 15.13 3.25 0 1.45 0
Breakpoint 1, toplevel at funds:65
funds:66: a.fill(0)
(rdb:1) p a
["ACFFX", "25.01", "15.13", "3.25", 0, "1.45", 0]
(rdb:1) p b
[["ACFFX", "25.01", "15.13", "3.25", 0, "1.45", 0], ["ACFFX", "25.01",
"15.13", "3.25", 0, "1.45", 0], ["ACFFX", "25.01", "15.13", "3.25", 0,
"1.45", 0], ["ACFFX", "25.01", "15.13", "3.25", 0, "1.45", 0]]
(rdb:1) c
ACINX 28.53 15.63 3.95 7.76 1.05 0
Breakpoint 1, toplevel at funds:65
funds:65: b.push(a)
(rdb:1) p a
["ACINX", "28.53", "15.63", "3.95", "7.76", "1.05", 0]
(rdb:1) p b
[["ACINX", "28.53", "15.63", "3.95", "7.76", "1.05", 0], ["ACINX",
"28.53", "15.63", "3.95", "7.76", "1.05", 0], ["ACINX", "28.53",
"15.63", "3.95", "7.76", "1.05", 0], ["ACINX", "28.53", "15.63", "3.95",
"7.76", "1.05", 0]]
(rdb:1) exit
[Abba:~/ruby] josephal% cat funds
#!/usr/bin/ruby

....

a = Array.new(7, 0) # a is the line item
b = Array.new # b is the array of arrays
....

r.each do |i|
    ...
        b.push(a)
    ...
end

eagle eyes joe wrote:

Greetings,

Moin.

In my program, I fill an array, a, with data and push it onto an array b which is initially empty.

You push the same array over and over. You want to push a copy instead. Use #clone or #dup to get a copy of the Array.

Regards,
Florian Gross

> In my program, I fill an array, a, with data and push it onto an array b
> which is initially empty.

You push the same array over and over. You want to push a copy instead.
Use #clone or #dup to get a copy of the Array.

Beware that may not be enough by itself, because the array contains strings;
even if you dup the array you will still have references to the same
strings. This will become apparent if you later modify one of the strings:

  a = ["one","two","three"]
  b =
  b << a.dup << a.dup
  b[0][0] << "XXX"
  p b
  # => [["oneXXX", "two", "three"], ["oneXXX", "two", "three"]]

But it's not an issue if you only ever replace the array elements with a
reference to another object, e.g.

  b[0][0] = "newstring"

A safer way is to fill the array directly:

  b =
  5.times do
    b << ["ACINX", "28.53", "15.63", "3.95", "7.76", "1.05", 0]
  end

Or more compactly,

  b = (1..5).collect { ["ACINX", "28.53", "15.63", "3.95", "7.76", "1.05", 0] }

That's because each time a constant like "ACINX" is executed, it creates a
brand new String object; equally, each time [ ... ] is executed it creates a
brand new Array object.

Alternatively, you can do a "deep copy" of the array and its contents. A
quick and fairly convenient way to do this is with Marshal, to serialise the
object and then create a fresh object tree from the serialised form.

  a = ["one","two","three"]
  b =
  b << Marshal.load(Marshal.dump(a)) << Marshal.load(Marshal.dump(a))
  b[0][0] << "XXX"
  p b
  # => [["oneXXX", "two", "three"], ["one", "two", "three"]]

Regards,

Brian.

Brian Candler wrote:

In my program, I fill an array, a, with data and push it onto an array b
which is initially empty.

You push the same array over and over. You want to push a copy instead.
Use #clone or #dup to get a copy of the Array.

Beware that may not be enough by itself, because the array contains strings;
even if you dup the array you will still have references to the same
strings. This will become apparent if you later modify one of the strings:

Thank you for the correction, you are right of course.

Or more compactly,

  b = (1..5).collect { ["ACINX", "28.53", "15.63", "3.95", "7.76", "1.05", 0] }

Or even this:

b = Array.new(5) { ["ACINX", "28.53", "15.63", "3.95", "7.76", "1.05", 0] }

Regards,
Brian.

More regards,
Florian Gross