Well, it's not that difficult to do in a template (but alas, I've done this sort of things many times):
use 5.020;
use warnings;
use Template;
my $data =
[[ qw(1 2 A 4) ],
[ qw(3 4 A 5) ],
[ qw(5 5 B 2) ],
[ qw(6 2 B 3) ],
[ qw(1 2 C 2) ],
[ qw(2 3 C 2) ],
[ qw(1 3 C 1) ],
];
my $template = <<'EOT';
[% SET category = '';
FOREACH line IN data;
IF line.2 != category;
GET line.2; "\n";
category = line.2;
END;
GET line.join(" "); "\n";
END;
%]
EOT
my $tt = Template->new;
$tt->process(\$template,{data => $data});
That said, if you're munging your data structure in Perl, then I'd suggest a different
structure with a hash as the top level, and lists of lists inside:
{ A => [[qw(1 2 A 4)],[qw(3 4 A 5)]],
B => [[qw(5 5 B 2)],[qw(6 2 B 3)]],
C => [[qw(1 2 C 2)],[qw(2 3 C 2)],[qw(1 3 C 1)]],
}
...which can then be processed with a different template. Note the extra
sort which is required because hash entries come in random order.
use 5.020;
use warnings;
use Template;
my $data =
[[ qw(1 2 A 4) ],
[ qw(3 4 A 5) ],
[ qw(5 5 B 2) ],
[ qw(6 2 B 3) ],
[ qw(1 2 C 2) ],
[ qw(2 3 C 2) ],
[ qw(1 3 C 1) ],
];
my %munged;
for my $record (@$data) {
no warnings "uninitialized";
push @{$munged{$record->[2]}}, $record;
}
my $template = <<'EOT';
[% FOREACH category IN data.keys.sort;
GET category; "\n";
FOREACH record IN data.$category;
GET record.join(" ");"\n";
END;
END;
%]
EOT
my $tt = Template->new;
$tt->process(\$template,{data => \%munged});