我有一个哈希表,其中键的值是其他哈希表。
例如:{'key' => {'key2' => {'key3' => 'value'}}}
如何遍历这个结构?
我有一个哈希表,其中键的值是其他哈希表。
例如:{'key' => {'key2' => {'key3' => 'value'}}}
如何遍历这个结构?
这个答案基于 Dave Hinton 的想法,即编写一个通用的子程序来遍历哈希结构。这样的哈希遍历器接受一个代码引用,并简单地为哈希中的每个叶子节点调用该代码。
采用这种方法,同一个哈希遍历器可以用于执行许多不同的操作,具体取决于我们给它的回调函数。为了实现更大的灵活性,您需要传递两个回调——一个用于在值是哈希引用时调用,另一个用于在它是普通标量值时调用。类似这样的策略在 Marc Jason Dominus 的优秀书籍《Higher Order Perl》中深入探讨。
use strict;
use warnings;
sub hash_walk {
my ($hash, $key_list, $callback) = @_;
while (my ($k, $v) = each %$hash) {
# Keep track of the hierarchy of keys, in case
# our callback needs it.
push @$key_list, $k;
if (ref($v) eq 'HASH') {
# Recurse.
hash_walk($v, $key_list, $callback);
}
else {
# Otherwise, invoke our callback, passing it
# the current key and value, along with the
# full parentage of that key.
$callback->($k, $v, $key_list);
}
pop @$key_list;
}
}
my %data = (
a => {
ab => 1,
ac => 2,
ad => {
ada => 3,
adb => 4,
adc => {
adca => 5,
adcb => 6,
},
},
},
b => 7,
c => {
ca => 8,
cb => {
cba => 9,
cbb => 10,
},
},
);
sub print_keys_and_value {
my ($k, $v, $key_list) = @_;
printf "k = %-8s v = %-4s key_list = [%s]\n", $k, $v, "@$key_list";
}
hash_walk(\%data, [], \&print_keys_and_value);
这符合你的需求吗?(未经测试)
sub for_hash {
my ($hash, $fn) = @_;
while (my ($key, $value) = each %$hash) {
if ('HASH' eq ref $value) {
for_hash $value, $fn;
}
else {
$fn->($value);
}
}
}
my $example = {'key' => {'key2' => {'key3' => 'value'}}};
for_hash $example, sub {
my ($value) = @_;
# Do something with $value...
};
这篇文章可能会有所帮助。
foreach my $key (keys %hash) {
foreach my $key2 (keys %{ $hash{$key} }) {
foreach my $key3 (keys %{ $hash{$key}{$key2} }) {
$value = $hash{$key}{$key2}->{$key3};
# .
# .
# Do something with $value
# .
# .
# .
}
}
}
Data::Visitor
看起来很有用。从文档中并不立即清楚如何做一些简单的事情--例如,遍历嵌套的哈希结构,打印叶子值及其键(直接和其祖先)。我相信这是可行的;只需要花点时间理解一下。 :) - FMc这并不是一个全新的答案,但我想分享如何递归地打印所有哈希值,并在需要时修改它们。
这是对dave4420答案的微小修改,在其中将值作为引用传递给回调函数,以便我的回调程序可以修改哈希中的每个值。
我还必须重建哈希表,因为while each循环创建的是副本而不是引用。
sub hash_walk {
my $self = shift;
my ($hash, $key_list, $callback) = @_;
while (my ($k, $v) = each %$hash) {
# Keep track of the hierarchy of keys, in case
# our callback needs it.
push @$key_list, $k;
if (ref($v) eq 'HASH') {
# Recurse.
$self->hash_walk($v, $key_list, $callback);
}
else {
# Otherwise, invoke our callback, passing it
# the current key and value, along with the
# full parentage of that key.
$callback->($k, \$v, $key_list);
}
pop @$key_list;
# Replace old hash values with the new ones
$hash->{$k} = $v;
}
}
hash_walk(\%prj, [], \&replace_all_val_strings);
sub replace_all_val_strings {
my ($k, $v, $key_list) = @_;
printf "k = %-8s v = %-4s key_list = [%s]\n", $k, $$v, "@$key_list";
$$v =~ s/oldstr/newstr/;
printf "k = %-8s v = %-4s key_list = [%s]\n", $k, $$v, "@$key_list";
}
Data::Visitor
和Data::Deep
之外,还有超级简单的Data::Traverse
:use Data::Traverse qw(traverse);
my %test_hash = (
q => [qw/1 2 3 4/],
w => [qw/4 6 5 7/],
e => ["8"],
r => {
r => "9" ,
t => "10" ,
y => "11" ,
} ,
);
traverse { return if /ARRAY/; print "$a => $b\n" if /HASH/ && $b > 8 } \%test_hash;
Output:
t => 10
y => 11
$a
和 $b
在这里被视为特殊变量(与 sort()
一样),在 traverse()
函数内部也是如此。 Data::Traverse
是一个非常简单但极其有用的模块,没有非核心依赖项。
你需要循环两次。
while ( ($family, $roles) = each %HoH ) {
print "$family: ";
while ( ($role, $person) = each %$roles ) {
print "$role=$person ";
}
print "\n";
}
foreach my $keyname (keys(%foo) {
my $subhash = $foo{$keyname};
# stuff with $subhash as the value at $keyname
}