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

As a follow-up to vladb's answer to my previous question here's next my question:

I've implemented vladb's code in a little different way, however using the same push calls, and my structure doesn't come out as I want it.

My code is as follows:
opendir(DIRHANDLE, $self->param('photos_dir')) or die "couldn't open m +ain directory: $!"; $dir_row_count = 0; $file_count = 0; $row_limit = 5; while (defined($filename = readdir(DIRHANDLE))) { next if $filename =~ /^\.\.?$/ || !(-d $filename); push @$dir_list, { dir => $filename, dir_row => [] }; opendir(SUBDIRHANDLE, "$filename") or die "couldn't open directory + $filename: $!"; my @pictures = grep { /\.(?:png|gif|jpg)$/ } readdir SUBDIRHAN +DLE; closedir(SUBDIRHANDLE); push @{$dir_list->[scalar @$dir_list - 1]{dir_row}}, { file_row => + [] }; $file_count = 0; foreach (@pictures) { unless ($file_count % $row_limit) { push @{$dir_list->[scalar @$dir_list - 1]{dir_row}}, { fil +e_row => [] }; } push @{$dir_list->[scalar @$dir_list - 1]{dir_row}[$dir_row_co +unt]{file_row}}, { filename => "$filename/$_", alt => $_ }; $file_count++; } $dir_row_count++; }
Using data::dumper i get:
$VAR1 = [ { 'dir' => '50s', 'dir_row' => [ { 'file_row' => [ { 'alt' => 'band_table.jp +g', 'filename' => '50s/band +_table.jpg' }, { 'alt' => 'cake_t2.jpg', 'filename' => '50s/cake +_t2.jpg' }, { 'alt' => 'cake-t.jpg', 'filename' => '50s/cake +-t.jpg' }, { 'alt' => 'an_logo.jpg', 'filename' => '50s/an_l +ogo.jpg' }, { 'alt' => 'gabe-bpm.jpg' +, 'filename' => '50s/gabe +-bpm.jpg' }, { 'alt' => 'cake_enter.jp +g', 'filename' => '50s/cake +_enter.jpg' } ] }, { 'file_row' => [] }, { 'file_row' => [] } ] }, { 'dir' => '60s', 'dir_row' => [ { 'file_row' => [] }, { 'file_row' => [ { 'alt' => 'cake_cdsleeve +.jpg', 'filename' => '60s/cake +_cdsleeve.jpg' } ] } ] } ];


I get too many file_rows in the first dir_row (should be 2), and there should only be 5 images per file_row. In the 2nd dir, it starts with an empty file_row..*shrug*

What am I doing wrong? Any help would be appreciated.

-Brian

Edit kudra, 2002-06-03 Added title given in ntc

Replies are listed 'Best First'.
(jeffa) Re: Structure for nested html::template loops
by jeffa (Bishop) on Jun 02, 2002 at 01:27 UTC
    Here is my take on the problem, but first i gotta share this with you. Take a list of numbers and break them up into a two dimensional array, each inner array contains, say 4 elements. Here was my first naive try:
    use strict; use Data::Dumper; my $i = 0; my (@tab,@row); for (0..10) { unless ($i % 4 or $i == 0) { push @tab,[@row]; @row = (); } push @row,$_; $i++; } push @tab,[@row]; print Dumper \@tab;
    In a nutshell, we iterate through the list. Unless we have reached the 4th element, we store that element in another transitory list. If we have reached the 4th element, we store that list into our outer array. This was the logic i started with trying to solve your problem. It is rather inelegant as i have to push that final transitory array after the loop is finished - and that's what bit me when i applied it to this HTML::Template problem.

    Now, consider this approach instead:

    use strict; use Data::Dumper; my ($i,$j) = (0,0); my $tab; for (0..10) { push @{$tab->[$j]},$_; $j++ unless ++$i % 4; } print Dumper $tab;
    Wow, so much simpler. This takes advantage of Perl's auto-vivification abilities. Instead of depending upon different states, we just do it - and we don't have to worry about clearing a transitory array, but we do have an extra counter for iteration. I'll take that extra counter for ease of programming.

    So, here is my solution to this larger problem at hand. You will have to make modifications to suite your needs, but this should show you how to do it. I ran this code from my public_html directory on my images directory. That directory contained 3 subdirs with a different numbers of images in each one. I also bundled the template file into the code via the DATA filehandle.

    use strict; use HTML::Template; use File::Basename; my $row_limit = 5; my $image_dir = 'images'; my %ok_ext = map { $_ => 1 } qw(jpg gif png); my $data = do {local $/; <DATA>}; my $template = HTML::Template->new( scalarref => \$data, ); my @dir_row; while(my $dir = <$image_dir/*>) { next unless -d $dir; my ($i,$j) = (0,0); my $dir_row = {dir => $dir}; while (my $full = <$dir/*>) { my ($file,$ext) = (fileparse($full,keys %ok_ext))[0,2]; next unless $ok_ext{$ext}; push @{$dir_row->{file_row}->[$j]->{images}}, { filename => $full, alt => $file }; $j++ unless ++$i % $row_limit; } push @dir_row, $dir_row; } $template->param( title => 'Images', dir_row => \@dir_row, ); print $template->output; __DATA__ <html> <head> <title><tmpl_var title></title> </head> <body> <center> <tmpl_loop dir_row> <table border="1"> <caption><tmpl_var dir></caption> <tmpl_loop file_row> <tr> <tmpl_loop images> <td><img src="<tmpl_var filename>" alt="<tmpl_var alt>"> </td> </tmpl_loop> </tr> </tmpl_loop> </tmpl_loop> </table> </center> </body> </html>

    jeffa

    L-LL-L--L-LL-L--L-LL-L--
    -R--R-RR-R--R-RR-R--R-RR
    B--B--B--B--B--B--B--B--
    H---H---H---H---H---H---
    (the triplet paradiddle with high-hat)
    
Data::Grouper preps data for for HTML::Template
by markjugg (Curate) on Sep 01, 2002 at 23:50 UTC
    See also Data::Grouper, which was built to transform data structures into the format needed for a nested loop in HTML::Template. I use it frequently to transform data from a database via DBI into H::T's nested loop format.

    -mark