Perl:从复杂散列将输出格式化为表格格式

Perl:从复杂散列将输出格式化为表格格式,perl,formatting,Perl,Formatting,我有一个如下的结构 my %hash = ( 'P' => { '4' => [ '1/4/1 2', '1/4/1 3' ], '2' => [ '1/2/1 1'

我有一个如下的结构

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";
    }
}
my%seed;
my@headers=sort{$a$b}grep{!$seen{$}++}map{keys%{$hash{$}}}keys%hash;
打印“节点”,“x 30,联接”(“x 10,@headers)”,“\n”;
foreach my$节点(键%hash){
我的价值观;
foreach my$num(键%{$hash{$node}}){
foreach my$klm(@{$hash{$node}{$num}){
推送(@值,$klm);
}
}
foreach我的$i(0..$#值){
打印“$node$values[$i]\n”;
}
}
欢迎进一步建议。

为什么不试试:

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";
}
my%seed;
my@headers=sort{$a$b}grep{!$seen{$}++}map{keys%{$hash{$}}}keys%hash;
printf(“%-25s”,“节点”);
foreach my$节点(@headers)
{
printf(“%-25s”,$node);
}
打印“\n”;
foreach my$节点(键%hash){
printf“%-25s”,$node;
foreach my$num(@headers)
{   
if(已定义($hash{$node}{$num}))
{
my$string=join(“,@{$hash{$node}{$num});
printf“%-25s”,$string;
}
其他的
{
printf“%-25s”,“”;
}      
}
打印“\n”;
}
@辛巴布谢谢你的评论


这个版本已经过调试和测试

我直接使用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;
    }
}
使用严格;
使用“全部”警告;
使用Excel::Writer::XLSX;
我的%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@headers=sort{$a$b}
grep{!$seen{$}++}
映射{keys%{$hash{${}}
密钥%hash;
#计算每个节点有多少行
我的%u行数;
foreach my$节点(键%hash){
计数行:foreach my$头(键%{$hash{$node}}){
$number_of_rows{$node}=scalar@{$hash{$node}->{$header};
最后计数行数;
}
}
my$workbook=Excel::Writer::XLSX->new('test.XLSX');
我的$工作表=$工作簿->添加工作表;
#写标题并保存cols
我的%colu标题;
$worksheet->write(0,0,'节点');
对于(我的$i=0;$i写入(0,$i+1,$headers[$i]);
}
my$row=1;#Excel文件中的总行
my$node_row=0;#当前节点的当前行
节点:foreach my$节点(排序键%hash){
#写入节点值(字母P,Q)
$worksheet->write($row,0,$node);
#迭代列。。。
foreach my$header(@headers){
#…但只写一个有值的
$worksheet->write(
$row,
$col_headers{$header},
$hash{$node}->{$header}->[$node\u行]
)如果存在$hash{$node}->{$header};
}
$row++#始终转到新行
if(++$node_行<$number_行{$node}){
#但是如果我们没有完成当前节点的所有行,
#使用下一个节点\行在新行中重做此节点
重做节点;
}
否则{
#或者重置节点\行
$node_row=0;
}
}
这将使用您的代码查找所有标题。它指定哪个标题包含哪个列,并计算每个节点的行数。然后,它迭代节点,使用继续运行一个节点,直到该节点的所有行都已用尽。它通过保持当前节点行的递增计数器来完成此操作。同时,我们始终增加整个行计数器,使其在表中向下移动

这是它的样子


代码有点凌乱,但它完成了任务。看起来这是一个一次性运行的任务。

输出用于屏幕,而不是CSV阅读器?@simbabque最后,我需要将其放入excel。示例输出完成了吗?是的,我相信是的。如果您有任何疑问,请告诉我?p下的所有数组是否总是有两个和两个Q下的所有数组都始终有一个元素?我不知道该如何使用。请提供一个完整的工作示例。您现在缺少一个右卷发,一旦添加它,它将以
Q数组(0x1cc0b20)\nARRAY(0x1cc09a0)