... 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(); #### A B C D E #### [ { 'COLUMN_LOOP' => [ {'data' => 'A'}, {'data' => 'B'}, {'data' => 'C'} ] }, { 'COLUMN_LOOP' => [ {'data' => 'D'}, {'data' => 'E'} ] } ]; #### A C E B D #### [ { 'COLUMN_LOOP' => [ {'data' => 'A'}, {'data' => 'C'}, {'data' => 'E'} ] }, { 'COLUMN_LOOP' => [ {'data' => 'B'}, {'data' => 'D'} ] } ]; #### So if your existing TMPL_LOOP section looks like: "> Change it to: "> ____ #### #!/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->[$index]; } } return \@result_rows; } ____ #### columnize(, 5, "down"); A literal interpretation would produce: A D F H J B E G I K C #### A D G J B E H K C F I