How to iterate through Hash (of Hashes) in Perl?

2019-01-21 17:30发布

I have Hash where values of keys are other Hashes.

Example: {'key' => {'key2' => {'key3' => 'value'}}}

How can I iterate through this structure?

9条回答
爷、活的狠高调
2楼-- · 2019-01-21 18:04

This answer builds on the idea behind Dave Hinton's -- namely, to write a general purpose subroutine to walk a hash structure. Such a hash walker takes a code reference and simply calls that code for each leaf node in the hash.

With such an approach, the same hash walker can be used to do many things, depending on which callback we give it. For even more flexibility, you would need to pass two callbacks -- one to invoke when the value is a hash reference and the other to invoke when it is an ordinary scalar value. Strategies like this are explored in greater depth in Marc Jason Dominus' excellent book, 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);
查看更多
贼婆χ
3楼-- · 2019-01-21 18:04

The earlier answers show how to roll your own solution, which is good to do at least once so you understand the guts of how perl references and data structures work. You should definitely take a read through perldoc perldsc and perldoc perlref if you haven't already.

However, you don't need to write your own solution -- there is already a module on CPAN which will iterate through arbitrarily-complex data structures for you: Data::Visitor.

查看更多
够拽才男人
4楼-- · 2019-01-21 18:11

You will have to loop through it twice. i.e.

while ( ($family, $roles) = each %HoH ) {
   print "$family: ";
   while ( ($role, $person) = each %$roles ) {
      print "$role=$person ";
   }
print "\n";
}
查看更多
姐就是有狂的资本
5楼-- · 2019-01-21 18:11
foreach my $keyname (keys(%foo) {
  my $subhash = $foo{$keyname};
  # stuff with $subhash as the value at $keyname
}
查看更多
我命由我不由天
6楼-- · 2019-01-21 18:14

If you are using perl as a "CPAN interpreter" then in addition to Data::Visitor and Data::Deep there is the super simple 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 { next if /ARRAY/; print "$a => $b\n" if /HASH/ && $b > 8 } \%test_hash;

Output:

t => 10
y => 11

$a and $b are treated as special variables here (as with sort()) while inside the traverse() function. Data::Traverse is a very simple but immensely useful module with no non-CORE dependencies.

查看更多
乱世女痞
7楼-- · 2019-01-21 18:17

Also, please read through perldoc perldsc. You can learn about hashes in depth

查看更多
登录 后发表回答