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

Fellow Monasterians

In preparation for a nested HTML::Template tmpl_loop I loop through the AoH results of a MySQL query attempting to add a second AoH to a key of the original AoH. (I whittled this down as much as possible)

my $stmt = 'SELECT projects FROM time_sheet WHERE subscriber_id = ?'; + my $proj = $dbh->selectall_arrayref($stmt, {Slice => {}}, $subscriber_ +id); $stmt = 'SELECT users FROM time_sheet project_id = ?'; for my $i ( 0 .. $#$proj ) { my $time = $dbh->selectall_arrayref($stmt, {Slice => {}}, $project_ +id); for my $j ( 0 .. $#$time ) { push @users, $time->[$j]; } $proj->[$i]{'users'} = \@users; undef @users; }

But a Data::Dumper produces a bizarre value for users =>

print Dumper($proj); $VAR1 = [ { 'number' => 'CGL00507', 'project_title' => 'Website', 'subtotal' => 2, 'users' => $VAR1->[0]{'users'}, 'project_id' => '7599' } ];

Strange, because when I run a separate test:

my $AoH_class = [ { 'class' => 'Algebra', 'room' => '204', }, { 'class' => 'Science', 'room' => '413', } ]; my @AoH_algebra = ( { 'name' => 'Fred', 'level' => 'junior', }, { 'name' => 'Barney', 'level' => 'senior', } ); $AoH_class->[1]{'students'} = \@AoH_algebra; print Dumper($AoH_class);

I get the desired results of:

$VAR1 = [ { 'class' => 'Algebra', 'room' => '204' }, { 'students' => [ { 'level' => 'junior', 'name' => 'Fred' }, { 'level' => 'senior', 'name' => 'Barney' } ], 'class' => 'Science', 'room' => '413' } ];

What am I missing, fellow monks? TIA

—Brad
"The important work of moving the world forward does not wait to be done by perfect men." George Eliot

Replies are listed 'Best First'.
Re: Adding an AoH to an AoH
by ikegami (Patriarch) on Jan 21, 2008 at 03:21 UTC

    You are storing multiple references to the same variable into your structure, but you meant to store references to different arrays.

    for my $i ( 0 .. $#$proj ) { my $time = $dbh->selectall_arrayref($stmt, {Slice => {}}, $project_ +id); my @users; # Create a new array every pass through the loop. for my $j ( 0 .. $#$time ) { push @users, $time->[$j]; } $proj->[$i]{'users'} = \@users; }

    By the way, undef @users; is also a bug, since it clears both @users and @{ $proj->[$i]{'users'} } since they're the same variable.

    Update: The above code can be simplified to

    for my $i ( 0 .. $#$proj ) { my $time = $dbh->selectall_arrayref($stmt, {Slice => {}}, $project_ +id); $proj->[$i]{'users'} = $time; }

      Just a word of caution: do not directly store off the returned reference from DBI results from other methods without first checking the docs to see if it's safe to do so.

      Most likely to be hit in "normal" use would be fetchrow_arrayref which reuses the same underlying array for each call (so you'd need to dereference and make a copy; e.g. $store->{'somewhere'} = [ @{ $returned_by_fetchrow_arrayref } ];).

      The cake is a lie.
      The cake is a lie.
      The cake is a lie.

      Thanks ikegami for pointing out that very subtle bug of the undef and suggesting just recreating the @users array for each pass. Brilliant. I don't know if I would have ever seen that. Anyway, all is well and working as hoped.

      Which brings up another point: create a test that really emulates the actual application. In this case my test was too sanitized and would never have shown me the problem.

      —Brad
      "The important work of moving the world forward does not wait to be done by perfect men." George Eliot

        Indeed. Minimizing the code is a great way of isolating a problem.

        Another good way of finding sticky problems is trying to describe the problem to someone else. Follow the description by as man "It can't be X because I tried Y." as you can think of. When doing so, I usually come up with Xs I hadn't handled.