在Perl中打印数组哈希散列的有效方法

时间:2014-09-10 14:25:20

标签: perl data-structures hash

我正在编写一个脚本,它涉及打印数组哈希散列的内容。

ex(伪代码):

my %hash = ();
$hash{key1}{key2} = ['value1', 'value2', 'value3', . . .];

$hash{key1}{key2} = @array_of_values;

基本上我希望能够为任意数量的键组合执行此操作,并能够遍历所有可能的键/值对(或者可能更正确地表示为键,键/数组对,因为每个值实际上是一个值数组,每个数组有2个与之关联的键),并按以下格式打印输出:

" key1,key2,value1,value2,value3 ,. 。 。\ n"

例如:

#!/usr/bin/perl

use strict;
use warnings;

# initialize hash
my %hash = ();
my $string1 = "string1";
my $string2 = "string2";

# push strings onto arrays stored in hash
push @{$hash{a}{b}}, $string1;
push @{$hash{a}{b}}, $string2;
push @{$hash{c}{d}}, $string2;
push @{$hash{c}{d}}, $string1;

# print elements of hash
# (want to do this as a loop for all possible key/value pairs)
local $, = ',';
print "a, b, ";
print @{$hash{a}{b}};
print "\n";
print "c, d, ";
print @{$hash{c}{d}};
print "\n";

system ('pause');

此代码的输出如下所示:

a, b, string1,string2
c, d, string2,string1
Press any key to continue . . .

我正在考虑使用each运算符,但它似乎只适用于一维哈希。 (each只返回一个键值对,当涉及2个键时它无法正常工作)

如何简化此代码以遍历循环中的散列并打印所需的输出,无论我的散列有多大?

7 个答案:

答案 0 :(得分:5)

即使对于多级散列,使用each也可以正常工作,您只需要确保参数是散列。这是一个如何做到这一点的例子。我还展示了如何初始化你的哈希值。

use strict;
use warnings;
use v5.14;

my $string1 = "string1";
my $string2 = "string2";
my %hash = (
    a => { 
        b => [ $string1, $string2 ],
    },
    c => {
        d => [ $string2, $string1 ],
    }
);

for my $key (keys %hash) {
    while (my ($k, $v) = each %{ $hash{$key} }) {
        say join ", ", $key, $k, @$v;
    }
}

<强>输出:

c, d, string2, string1
a, b, string1, string2

请注意使用@$v到达最内层数组的简单性,而不是有点麻烦的替代@{ $hash{$key}{$k} }

答案 1 :(得分:4)

这对于递归子例程来说是最整洁的,因为它只会递归几次,所以它不是一个浪费的解决方案。

use strict;
use warnings;

my %hash;
my ($string1, $string2) = qw/ string1 string2 /;

push @{$hash{a}{b}}, $string1;
push @{$hash{a}{b}}, $string2;
push @{$hash{c}{d}}, $string2;
push @{$hash{c}{d}}, $string1;

print_data(\%hash);

sub print_data {
    my ($ref, $prefix) = (@_, '');

   if (ref $ref eq 'ARRAY') {
      print $prefix, join(', ', @$ref), "\n";
   }
   else {
      print_data($ref->{$_}, "$prefix$_, ") for sort keys %$ref;
   }
}

<强>输出

a, b, string1, string2
c, d, string2, string1

答案 2 :(得分:3)

您还没有说明为什么要打印HoHoA的内容(数组哈希值)。如果是出于调试目的,我可以使用Data :: Dumper(核心)或Data :: Dump(在CPAN上)。

use Data::Dumper qw(Dumper);
print Dumper \%hash;

use Data::Dump qw(pp);
pp \%hash;

假设您希望按照样本格式化输出(并且它始终只是HoHoA),我会使用嵌套循环:

while (my ($ok, $ov) = each %hash) {
    while (my ($ik, $iv) = each %$ov) {
        say join ',', @$iv;
    }
}

我不建议使用map。它最适合用于列表转换而不是流控制,并且使用map块嵌套$_块来跟踪外键和内键是不方便的。因为你和G. Cito表示有兴趣看到一个,但这里是:

say foreach map {
    my $o = $_;
    map {
        my $i = $_;
        join ',', $o, $i, @{$hash{$o}{$i}}
    } keys %{$hash{$o}};
} keys %hash;

答案 3 :(得分:2)

要打印数组哈希的哈希值,您可以使用foreachfor my遍历数据结构:

# keys of the outer hash
foreach my $oh (keys %hash) {
    # keys of the inner hash
    foreach my $ih (keys %{$hash{$oh}}) {
        # $hash{$oh}{$ih} is the array, so it can be printed thus:
        print join(", ", $oh, $ih, @{$hash{$oh}{$ih}}) . "\n";

        # or you can iterate through the items like this:
        # foreach my $arr (@{$hash{$oh}{$ih}})
        # {   doSomethingTo($arr); }

    }
}

对于所有制图爱好者来说,这里有一个map版本:

map { my $oh = $_; 
    map { say join( ", ", $oh, $_, @{$hash{$oh}{$_}} )  } keys %{$hash{$_}}
} keys %hash;

答案 4 :(得分:1)

我一直在考虑它,并想出了一个可能的解决方案。如果有人有更优雅的解决方案,我很乐意看到它。谢谢!

以下是我提出的解决方案:

#!/usr/bin/perl

use strict;
use warnings;

# initialize hash
my %hash = ();
my $string1 = "string1";
my $string2 = "string2";

# push strings onto arrays stored in hash
push @{$hash{a}{b}}, $string1;
push @{$hash{a}{b}}, $string2;
push @{$hash{c}{d}}, $string2;
push @{$hash{c}{d}}, $string1;

# prints hash of hash of arrays in a loop :)
my @keys1 = keys %hash;
for my $key1 (@keys1) {
    my @keys2 = keys $hash{$key1};
    for my $key2 (@keys2) {
        local $" = ', ';
        print "$key1, $key2, @{$hash{$key1}{$key2}}";
        print "\n";
    }
}

system ('pause');

此解决方案并不关心键的顺序,因此它将以随机顺序打印。

输出:

c, d, string2, string1
a, b, string1, string2
Press any key to continue . . .

答案 5 :(得分:1)

听起来你需要一个嵌套循环...这可能是一种用map来做这件事的方法,这个方法难以记忆且不太可读;-)(当然只对我说话 - 我期待看到map解决方案出现在这里!):

#!perl -l
my %hash = ();
$hash{key1}{key2} = ['value1', 'value2', 'value3',];

for my $outer (keys %hash) { 
    for my $inner (keys $hash{$outer}) { 
       print join(', ', $outer, $inner, @{ $hash{$outer}{$inner} } )
    } 
}

<强>输出:

key1, key2, value1, value2, value3

如果事情真的很复杂,还有Data::PrinterData::SeekData::Dive以及其他一些便利模块。有些人需要使用哈希引用,虽然这可能会使更简单的解决方案复杂化,或者更容易实现更大更困难的问题......

干杯,

编辑:移动&#34;嵌套地图&#34;单独回答。

答案 6 :(得分:1)

我问了它: - ( ......一个“嵌套地图”(??)。

#!perl -l
my %hash = ();
$hash{key1}{key2} = ['value1', 'value2', 'value3',];
map{ &{ sub{
   map{ print for @{$hash{$_[0]}{$_}}} keys $hash{$_} }}($_)} keys %hash;

输出

value1
value2
value3

NB:我认为从技术角度(幕后)它仍然是一个循环而且效率不高。看来如果你“调用”map的调用并用内容map{}包含匿名sub{& sub { ..位),那么你可以引用外部{{} 1}}主题值来自map({em> ie 在@_中,因为它们进入内部$_[0] ...但仅当它们位于列表上下文中{ {1}} ...错误(?!?)好的,我只是抓住这里。

无论如何,它起作用但看起来比它应该更麻烦 - 可能是因为它是“anti-pattern”。一个好处是它使“嵌套map循环”看起来更简洁,更容易阅读。