我有一个像下面的结构。
my %hash = (
'P' => {
'4' => [
'1/4/1 2',
'1/4/1 3'
],
'2' => [
'1/2/1 1',
'1/2/5 4'
]
},
'Q' => {
'4' => [
'1/4/1 3'
],
'3' => [
'1/3/1 1'
],
'5' => [
'1/5/1 1'
]
},
);
我想格式化它。
Node 2 3 4 5
P 1/2/1 1 1/4/1 2
P 1/2/5 4 1/4/1 3
Q 1/3/1 1 1/4/1 3 1/5/1 1
我目前正在努力。最后,我需要把它放到excel中。我想这样的事情可能会有一些调整。
my %seen;
my @headers = sort { $a <=> $b } grep {!$seen{$_}++} map{ keys %{$hash{$_}} }keys %hash;
print "node", ' ' x 30, join(" " x 10, @headers),"\n";
foreach my $node (keys %hash) {
my @values;
foreach my $num (keys %{$hash{$node}}) {
foreach my $klm (@{$hash{$node}{$num}}) {
push (@values,$klm);
}
}
foreach my $i (0 .. $#values) {
print "$node $values[$i]\n";
}
}
进一步的建议表示赞赏。
答案 0 :(得分:2)
我已经直接使用Excel文件完成了这项工作,因为我认为使用行和列并定位单个字段比直接处理输出更容易。
use strict;
use warnings 'all';
use Excel::Writer::XLSX;
my %hash = (
'P' => {
'4' => [ '1/4/1 2', '1/4/1 3' ],
'2' => [ '1/2/1 1', '1/2/5 4' ]
},
'Q' => {
'4' => ['1/4/1 3'],
'3' => ['1/3/1 1'],
'5' => ['1/5/1 1']
},
);
my %seen;
my @headers = sort { $a <=> $b }
grep { !$seen{$_}++ }
map { keys %{ $hash{$_} } }
keys %hash;
# count how many rows each node has
my %number_of_rows;
foreach my $node ( keys %hash ) {
COUNT_ROWS: foreach my $header ( keys %{ $hash{$node} } ) {
$number_of_rows{$node} = scalar @{ $hash{$node}->{$header} };
last COUNT_ROWS;
}
}
my $workbook = Excel::Writer::XLSX->new('test.xlsx');
my $worksheet = $workbook->add_worksheet;
# write the headers and save the cols
my %col_headings;
$worksheet->write( 0, 0, 'Node' );
for ( my $i = 0; $i <= $#headers; $i++ ) {
$col_headings{ $headers[$i] } = $i + 1;
$worksheet->write( 0, $i + 1, $headers[$i] );
}
my $row = 1; # overall row in the Excel file
my $node_row = 0; # current row for the current node
NODE: foreach my $node ( sort keys %hash ) {
# write the node value (letter P, Q)
$worksheet->write( $row, 0, $node );
# iterate the columns ...
foreach my $header (@headers) {
# ... but only write one that has a value
$worksheet->write(
$row,
$col_headings{$header},
$hash{$node}->{$header}->[$node_row]
) if exists $hash{$node}->{$header};
}
$row++; # always go to a new row
if ( ++$node_row < $number_of_rows{$node} ) {
# but if we have not done all the rows for the current node,
# redo this node in a new row with the next node_row
redo NODE;
}
else {
# or reset the node_row
$node_row = 0;
}
}
这将使用您的代码查找所有标头。它指定哪个标题包含哪个列,并计算每个节点的行数。然后,它使用redo
迭代节点,继续使用一个节点,直到该节点的所有行都已用完为止。它通过保持当前节点行的计数器来增加。与此同时,我们总是递增整个行计数器,使其在表格中向下移动。
这就是它的样子。
代码有点乱,但它完成了工作。无论如何,这看起来像是一次又一次忘记的任务。
答案 1 :(得分:0)
为什么不尝试:
my %seen;
my @headers = sort { $a <=> $b } grep {!$seen{$_}++} map{ keys %{$hash{$_}} }keys %hash;
printf("%-25s", "node");
foreach my $node (@headers)
{
printf("%-25s", $node);
}
print "\n";
foreach my $node (keys %hash) {
printf "%-25s", $node;
foreach my $num (@headers)
{
if(defined($hash{$node}{$num}))
{
my $string = join(" ", @{$hash{$node}{$num}});
printf "%-25s", $string ;
}
else
{
printf "%-25s", " " ;
}
}
print "\n";
}
@simbabque感谢您的评论
此版本已经过调试和测试