The output will look like:... my $flat_data = [{data=>'A'} , {data=>'B'}, {data=>'C'}, {data=>'D'},{data=>'E'}]; my $columnized_data = columnize($flat_data, 3, "across"); my $tmpl_file = ...; my $tmpl = HTML::Template->new( filename => $tmpl_file, loop_context_vars => 1 ) or die "Creation of template object from $tmpl_file failed."; $tmpl->param( {DATA_LOOP => $columnized_data}); print "Content-type: text/html\n\n", $tmpl->output();
In case you're wondering, the return value of columnize() was:A B C D E
Alternatively, had you called columnize($flat_data, 3, "down"), you'd get output:[ { 'COLUMN_LOOP' => [ {'data' => 'A'}, {'data' => 'B'}, {'data' => 'C'} ] }, { 'COLUMN_LOOP' => [ {'data' => 'D'}, {'data' => 'E'} ] } ];
with columnize() returning:A C E B D
Once you've added this second dimension to your data, you'll need to to do the same to your template. This is easy, since HTML::Template allows for nesting TMPL_LOOP tags. Simply add a nested TMPL_LOOP tag named COLUMN_LOOP, to your existing TMPL_LOOP. (The outer loop represents the rows, the inner loop, the columns).[ { 'COLUMN_LOOP' => [ {'data' => 'A'}, {'data' => 'C'}, {'data' => 'E'} ] }, { 'COLUMN_LOOP' => [ {'data' => 'B'}, {'data' => 'D'} ] } ];
The code:So if your existing TMPL_LOOP section looks like: <tmpl_loop name="DATA_LOOP"> <tr> <td> <tmpl_var name="data">"> </td> </tr> </tmpl_loop> Change it to: <tmpl_loop name="DATA_LOOP"> <tr> <tmpl_loop name="COLUMN_LOOP"> <td> <tmpl_var name="data">"> </td> </tmpl_loop> </tr> </tmpl_loop> ____
Caveat: when calling columnize with a "down" ordering, the column parameter is used as a maximum suggested column count, rather than a fixed specification. It calculates the number of rows that would result from the given number of columns, and then chooses an actual number of columns that would leave the fewest empty slots in the last row. Consider:#!/usr/bin/perl -w use strict; use Carp; sub columnize { my $rows_ref = shift; my $num_cols = shift; my $order = shift; if (!defined $rows_ref or !defined $num_cols or !defined $order) { croak("Undefined params not permitted:\n"); } if ($num_cols < 1) {croak "Bad column-number param: $num_cols\n";} if ($order !~ /^(across|down)$/i) {croak "Bad order param: $order\n +";} use integer; # use integer division for all division operations my $num_rows = ($#$rows_ref / $num_cols) + 1; my @result_rows = (); for (my $row = 0; $row < $num_rows; $row++) { for (my $col = 0; $col < $num_cols; $col++) { my $index; if ($order eq "across") { $index = ($row * $num_cols) + $col; } else { $index = ($col * $num_rows) + $row; } # Skip any empty spots in the last row (if "across") # or in the last column (if "down"): next if ($index > $#$rows_ref); $result_rows[$row]->{COLUMN_LOOP}->[$col] = $rows_ref->[$inde +x]; } } return \@result_rows; } ____
But if you're going to fill 3 rows anyway, and require the end-user to read vertically, then what you (or at least your reader) would probably want is:columnize(<data set with 11 items>, 5, "down"); A literal interpretation would produce: A D F H J B E G I K C
If you look at the output of the 'ls' command, you'll see that it follows a similar algorithm. Of course, this will usually only make a difference when you specify a lot of columns, and your data set is relatively small.A D G J B E H K C F I
In reply to Producing Columns with HTML::Template by hey_david
| For: | Use: | ||
| & | & | ||
| < | < | ||
| > | > | ||
| [ | [ | ||
| ] | ] |