my %column_width; foreach my $row (keys %data) { foreach my $column (keys %{$data{$row}}) { my $data_length = length($data{$row}{$column}); if (!exists($column_width{$column}) { # Make sure the label fits $column_width{$column} = length($column); } if ($column_width{$column} < $data_length) { $column_width{$column} = $data_length; } } } # Order the columns accordingly. You may need a better sort. my @columns = sort keys %column_width; print join (" | ", sprintf ("%15s", ""), map { sprintf ("%*s", $column_width{$_}, $_) } @columns; ),"\n"; foreach my $row (sort keys %data) { my $continued; my $line = 0; do { $continued = 0; print join (" | ", # Only title the first line (0th) sprintf ("%15s", $line? '' : $row), map { $continued++ if (defined($data{$row}{$_}[$line+1])); sprintf ("%*s", $column_width{$_}, defined($data{$row}{$_}[$line])? $data{$row}{$_}[$line] : ''); } @columns ), "\n"; $line++; } while ($continued); }