Perl:从复杂哈希中将输出格式化为表格形式

时间:2016-08-17 14:06:44

标签: perl formatting

我有一个像下面的结构。

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";
    }
}

进一步的建议表示赞赏。

2 个答案:

答案 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迭代节点,继续使用一个节点,直到该节点的所有行都已用完为止。它通过保持当前节点行的计数器来增加。与此同时,我们总是递增整个行计数器,使其在表格中向下移动。

这就是它的样子。

screenshot of open office

代码有点乱,但它完成了工作。无论如何,这看起来像是一次又一次忘记的任务。

答案 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感谢您的评论

此版本已经过调试和测试