in reply to Strange hash array behavior

As used, { } creates an (anonymous) hash, assigns the contents of the curlies to that hash, and returns a reference to that hash.

In the first snippet, you create a new hash in every pass of the loop. You assign a reference to the newly created hash to $r->{cat}. Thus, every record references a different hash.

In the second snippet, you create one anonymous hash outside the loop. You assign a reference to that hash to $r->{cat}. Thus, you have multiple records referencing that one hash.

Replies are listed 'Best First'.
Re^2: Strange hash array behavior
by Rodster001 (Pilgrim) on Jan 29, 2009 at 03:25 UTC
    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!
      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!