假设嵌套哈希结构%old_hash
..
my %old_hash;
$old_hash{"foo"}{"bar"}{"zonk"} = "hello";
..我们希望使用子&flatten(...)
“展平”(抱歉,如果这是错误的术语!)到非嵌套哈希,以便...
my %h = &flatten(\%old_hash);
die unless($h{"zonk"} eq "hello");
&flatten(...)
的以下定义可以解决问题:
sub flatten {
my $hashref = shift;
my %hash;
my %i = %{$hashref};
foreach my $ii (keys(%i)) {
my %j = %{$i{$ii}};
foreach my $jj (keys(%j)) {
my %k = %{$j{$jj}};
foreach my $kk (keys(%k)) {
my $value = $k{$kk};
$hash{$kk} = $value;
}
}
}
return %hash;
}
虽然给出的代码有效,但它不是非常易读或干净。
我的问题是双重的:
答案 0 :(得分:10)
您的方法不是最佳做法,因为它无法扩展。如果嵌套哈希是六个,十个级别深度怎么办?重复应该告诉你,递归例程可能就是你需要的。
sub flatten {
my ($in, $out) = @_;
for my $key (keys %$in) {
my $value = $in->{$key};
if ( defined $value && ref $value eq 'HASH' ) {
flatten($value, $out);
}
else {
$out->{$key} = $value;
}
}
}
或者,良好的现代Perl风格是尽可能使用CPAN。 Data::Traverse可以满足您的需求:
use Data::Traverse;
sub flatten {
my %hash = @_;
my %flattened;
traverse { $flattened{$a} = $b } \%hash;
return %flattened;
}
作为最后一点,通过引用传递哈希值通常更有效,以避免它们被扩展到列表中,然后再次变为哈希值。
答案 1 :(得分:3)
首先,我会使用perl -c
来确保它干净地编译,而不是。所以,我会添加一个尾随}
来进行编译。
然后,我将通过perltidy运行它以改进代码布局(缩进等)。
然后,我会运行perlcritic(以“苛刻”模式)自动告诉我它认为不好的做法。它抱怨说:
子程序不以“return”结束
更新:在我发布上述答案之后,OP基本上改变了每行代码,但我相信它仍然适用。拍摄移动目标并不容易:)
答案 2 :(得分:2)
您需要弄清楚您的方法存在的一些问题。首先,如果有两个叶节点具有相同的密钥,会发生什么?如果输出包含它们的列表,那么第二个clobber是第一个被忽略吗?这是一种方法。首先,我们使用递归函数构造一个关键值对的平面列表,以处理其他散列深度:
my %data = (
foo => {bar => {baz => 'hello'}},
fizz => {buzz => {bing => 'world'}},
fad => {bad => {baz => 'clobber'}},
);
sub flatten {
my $hash = shift;
map {
my $value = $$hash{$_};
ref $value eq 'HASH'
? flatten($value)
: ($_ => $value)
} keys %$hash
}
print join( ", " => flatten \%data), "\n";
# baz, clobber, bing, world, baz, hello
my %flat = flatten \%data;
print join( ", " => %flat ), "\n";
# baz, hello, bing, world # lost (baz => clobber)
修复可能是这样的,它将创建包含所有值的数组引用的散列:
sub merge {
my %out;
while (@_) {
my ($key, $value) = splice @_, 0, 2;
push @{ $out{$key} }, $value
}
%out
}
my %better_flat = merge flatten \%data;
在生产代码中,在函数之间传递引用会更快,但为了清楚起见,我在此省略了它。
答案 3 :(得分:1)
您是否打算最终获得原始哈希的副本或只是重新排序的结果?
您的代码以一个哈希(引用使用的原始哈希)开头,并生成两个副本%i
和%hash
。
声明my %i=%{hashref}
不是必需的。您正在将整个哈希复制到新哈希。在任何一种情况下(无论您是否需要副本),您都可以使用对原始哈希的引用。
如果哈希中的哈希值与父哈希值相同,那么您也会丢失数据。这是为了吗?