in reply to Re: Strange hash array behavior
in thread Strange hash array behavior

I am sorta following you... here is a much more testable example:
use strict; use warnings; use Data::Dumper; my $data1 = [ { "shape" => "round", "food" => "apple" }, { "shape" => "square", "food" => "pear" } ]; my $data2 = [ { "big" => "cow", "small" => "bunny" }, ]; my $combined; foreach my $r (@{$data1}) { $r->{sizes} = $data2; push(@{$combined},$r); } print Dumper($combined);
Which of course produces the same result just as you explain. But after messing with this I am not able to make it do what I want.

I suppose it is because I am not really grasping it totally, which bothers me because I thought I had references down.

Can you modify the code above to produce the result I am looking for... do you see what I am trying to do? $data2 needs to reside outside that loop and I am wanting this result:

$VAR1 = [ { 'food' => 'apple', 'shape' => 'round', 'sizes' => [ { 'small' => 'bunny', 'big' => 'cow' } ] }, { 'food' => 'pear', 'shape' => 'square', 'sizes' => [ { 'small' => 'bunny', 'big' => 'cow' } ] } ];
Thanks! Thanks!

Replies are listed 'Best First'.
Re^3: Strange hash array behavior
by Util (Priest) on Jan 29, 2009 at 05:02 UTC
    Can you modify the code above to produce the result I am looking for...?
    You are making a shallow copy. You need a deep copy.
    Change this:
    $r->{sizes} = $data2;
    to:
    $r->{sizes} = [ map { { %{$_} } } @{$data2} ];
    or
    use Clone qw( clone ); $r->{sizes} = clone($data2);
    or
    use Clone::PP qw( clone ); $r->{sizes} = clone($data2);
    or
    use Storable qw( dclone ); $r->{sizes} = dclone($data2);
      Okay, cool that worked. I guess I just don't really conceptually get why the first iteration works but after that I get a shallow copy.

      I think the reason why I am confused is because this works and it seems to be the same concept (to me):

      my $data1 = [ { "shape" => "round", "food" => "apple" }, { "shape" => "square", "food" => "pear" }, { "shape" => "oval", "food" => "grape" }, ]; my $test = $data1; my $test2 = $test; my $test3 = $test2; print Dumper($test3);
      Update: Or an even better example of why I am confused:
      my $data1 = [ { "shape" => "round", "food" => "apple" }, { "shape" => "square", "food" => "pear" }, { "shape" => "oval", "food" => "grape" }, ]; foreach (0..5) { my $copy = $data1; print Dumper($copy); }
      Update: And yet an even better example... why the heck does this work and not the previous examples?
      use Data::Dumper; my $data1 = [ { "shape" => "round", "food" => "apple" }, { "shape" => "square", "food" => "pear" }, { "shape" => "oval", "food" => "grape" }, ]; my $data2 = [ { "big" => "cow", "small" => "bunny" }, { "big" => "horse", "small" => "mouse" }, ]; foreach (0..2) { my $d = $data1->[$_]; $d->{sizes} = $data2; print Dumper($d); }
      Last Update!: Using the same data above, this does not work:
      my @array; foreach (0..2) { my $d = $data1->[$_]; $d->{sizes} = $data2; push(@array,$d); } print Dumper(\@array);
      But this does, so I guess I just don't understand why it (the above) works on the first iteration but not any after. Is that really behaving like it is suppose to?
      my @array; foreach (0..2) { my $d = $data1->[$_]; $d->{sizes} = $data2; push(@array,clone($d)); } print Dumper(\@array);
      Thanks again for the fix!

        my @array; foreach (0..2) { my $d = $data1->[$_]; $d->{sizes} = $data2; push(@array,$d); } print Dumper(\@array);

        The underlying problem is that you think $data2 contains a hash or array (depending on the snippet), but it doesn't. It contains a reference to a hash an an array.

        When you assign $data2 to something, you're copying the reference, that's it. All your questions boil down to "Why doesn't copy the referenced hash or array?", and the answer is always "Because you didn't ask it to."

        I just don't really conceptually get why the first iteration works but after that I get a shallow copy.

        You never get a copy of the array referenced by $data2, not even the first time through the loop. Every time through the loop you set $r->{sizes} to be a reference to the same original anonymous array. If you want a new array you have to create it. util has shown you several ways you can make a copy of the array referenced by $data2. Having created a copy of the original array, you can then set $r->{sizes} to be a reference to the copy.

        And yet an even better example... why the heck does this work and not the previous examples?
        It only *seems* to work, because you are dumping too little, too often.
        If you add print Dumper($data1); after the close of the foreach, you will see the $VAR1->[0]{'sizes'} back-pointers.