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

Fellow Monasterians,

In trying to avoid the dreaded symbolic variable (building a variable from another as recently discussed), I have generated some code I know can be made simpler (this code *does* work). It does seem like the perfect place for the symbolic vs. the hash method--partly because I already have a mish-mash of hashes to prep my data for HTML::Template.

I apologize for the lengthy code. This is greatly simplied--there are actually 14 AoHs to build. How can I get this widdled down to a few loops? TIA!

$template = HTML::Template->new(filename => "../articlesummary.tmpl"); my $subtopic = $query->param('sub'); (11a, 11b, 22, 33) #really 14 +of these my $stmt = "SELECT id,title,goal FROM articles WHERE goal LIKE '$subto +pic.%'"; my $sth = execute_it($dbh,$stmt); #prepare and execute sub; my $sqldata = $sth->fetchall_arrayref({}); if ($sqldata) { my ($ctr1,$ctr2,$ctr3,$ctr4); my ($data11a, $data11b, $data22, $data33); for my $i ( 0 .. $#$sqldata ) { if ($sqldata->[$i]{'goal'} eq "11a") { $data11a->[$ctr1]{id} = $sqldata->[$i]{'id'}; $data11a->[$ctr1]{title} = $sqldata->[$i]{'title'}; $data11a->[$ctr1]{summary} = $sqldata->[$i]{'summary'}; $ctr1++; } if ($sqldata->[$i]{'goal'} eq "11b") { $data11b->[$ctr1]{id} = $sqldata->[$i]{'id'}; $data11b->[$ctr1]{title} = $sqldata->[$i]{'title'}; $data11b->[$ctr1]{summary} = $sqldata->[$i]{'summary'}; $ctr2++; } if ($sqldata->[$i]{'goal'} eq "22") { $data23->[$ctr1]{id} = $sqldata->[$i]{'id'}; $data23->[$ctr1]{title} = $sqldata->[$i]{'title'}; $data23->[$ctr1]{summary} = $sqldata->[$i]{'summary'}; $ctr3++; } if ($sqldata->[$i]{'goal'} eq "33") { $data33->[$ctr1]{id} = $sqldata->[$i]{'id'}; $data33->[$ctr1]{title} = $sqldata->[$i]{'title'}; $data33->[$ctr1]{summary} = $sqldata->[$i]{'summary'}; $ctr4++; } } if ($data11a) { $template ->param(list11a => $data11a); } if ($data11b) { $template ->param(list11b => $data11b); } if ($data22) { $template ->param(list22 => $data22); } if ($data33) { $template ->param(list33 => $data33); } }

Would like to use something like:

my goals qw(11a, 11b, 22, 33); for my $i ( 0 .. $#$sqldata ) { for my $j ( 0 .. $#goals ) { if ($sqldata->[$i]{'goal'} eq $goals[$j]) { $data$goals[$j]->[$ctr]{id} = $sqldata->[$i]{'id'}; $ctr++; } } }

HTML: (14 of these)

<tmpl_if list11b> <tmpl_loop list11b> <h4><tmpl_var title></h4> <p><tmpl_var summary></p> <p><a href="cgi-bin/show.pl?id=<tmpl_var id>">Read article...</a +></p> </tmpl_loop> </tmpl_if>

—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: Bloated code of loops and conditionals
by tlm (Prior) on Apr 22, 2005 at 22:02 UTC

    Instead of array refs $data11a, $data11b, etc., make a hash whose keys are 11a, 11b, etc., and store the array refs in it. Then, instead of accessing $data11a->[ $n ] you'd access $data{ 11a }[ $n ]. Or use push to avoid explicit indices.

    # my %goals; @goals{ qw( 11a 11b 22 33 ) } = (); my @flds = qw( id title summary ); if ($sqldata) { my %data; for my $d ( @$sqldata ) { my $g = $d->{ 'goal' }; # next unless exists $goals{ $g }; my %h; @h{ @flds } = @{ $d }{ @flds }; # hash slice assignment push @{ $data{ $g } }, \%h; } for my $g ( keys %data ) { $template ->param( "list$g" => $data{ $g } ); } }
    If you really want to control the valid values for the "goal" parameters, then uncomment the two commented lines. This will cause any record with a "goal" not in this list to be ignored.

    Update: Filled in the details. Fixed typo pointed out by bradcathey.

    the lowliest monk

      Thanks tlm (the artist formerly known as PP). It looks brilliant, but a bit beyond my experience. I have a couple of questions.

      - in the line:

      next unless exists $goal { $g }

      should $goal be goals with an 's'?

      - not sure what is going on with the 'hash slice assignment' line

      I'll plug this into my code, but I just wanted to understand it at the same time.


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

        Regarding your first question, yes, that was a typo (now fixed).

        A hash slice is a way to access several values of a hash using a list of keys. For regular hashes, for example,

        @hash{ qw( foo bar baz ) } = ( 1, 2, 3 );
        has the same effect as
        $hash{ foo } = 1; $hash{ bar } = 2; $hash{ baz } = 3;
        You can use slices to copy several values from one hash to another. E.g.
        @keys = qw( foo bar baz ); @copy{ @keys } = @orig{ @keys };
        The last line copies three key-value pairs from %orig to %copy. In the code I posted earlier, the only difference is that the accessing of the RHS slice is done via a reference ($d) to a hash instead of a regular hash:
        @h{ @flds } = @{ $d }{ @flds }; # hash slice assignment
        Once you dereference a hash ref you can use it like any other hash.

        Slices are discussed in perldata, and using references in perlreftut and perlref.

        the lowliest monk

Re: Bloated code of loops and conditionals
by eibwen (Friar) on Apr 22, 2005 at 21:55 UTC
    Just a thought, but instead of the series of if statements, why don't you make a hash with keys $sqldata->[$i]{'goal'}?