Rodster001 has asked for the wisdom of the Perl Monks concerning the following question:

Greetings!

I am pulling my hair out trying to figure out why the following snippit:

foreach my $i (@{$t}) { my $id = $i->{id}; my $subs = $i->{subs}; my $name = $i->{name}; my $rv = $sth->execute($id); while (my $r = $sth->fetchrow_hashref) { $r->{cat} = ( { name=>$name,id=>$id } ); push (@{$p},$r); } return($p); }
Produces this (what I want):
$VAR1 = [ { 'cat' => { 'name' => 'Category', 'id' => '2558' }, 'name' => 'Product One', 'id' => '7669' }, { 'cat' => { 'name' => 'Category', 'id' => '2558' }, 'name' => 'Product Two', 'id' => '7670' }, ];
But this snippit:
foreach my $i (@{$t}) { my $id = $i->{id}; my $subs = $i->{subs}; my $name = $i->{name}; my $rv = $sth->execute($id); my $cat = ( { name => $name, id => $id } ); while (my $r = $sth->fetchrow_hashref) { $r->{cat} = $cat; push (@{$p},$r); } return($p); }
Produces this:
$VAR1 = [ { 'cat' => { 'name' => 'Category', 'id' => '2558' }, 'name' => 'Product One', 'id' => '7669' }, { 'cat' => $VAR1->[0]{'cat'}, 'name' => 'Product Two', 'id' => '7670' }, ];
Any enlightenment would be greatly appreciated!

Replies are listed 'Best First'.
Re: Strange hash array behavior
by ikegami (Patriarch) on Jan 29, 2009 at 00:12 UTC

    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.

      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);