Beefy Boxes and Bandwidth Generously Provided by pair Networks
XP is just a number
 
PerlMonks  

Iterate over HoH without Recursion

by chromatic (Archbishop)
on May 21, 2000 at 00:33 UTC ( #13940=snippet: print w/replies, xml ) Need Help??
Description: Recursion is expensive, but sometimes it's the quickest solution. Never one to walk away from a programming challenge (that I can actually envision), here's what you have to do to get at all of the keys in a Hash of Hashes of indeterminate nesting.

The sort is optional, as is what you do with the keys and values, once you find them. Making this into a subroutine and changing the output appropriately is left as an exercise for the reader.

#!/usr/bin/perl -w
use strict;
my %hash = (
    1 => 1,
    2 => { 3 => 4,
        5 => {
            6 => 7 },
        },
    8 => 9,
    10 => {},
);

my @stack;
push @stack, \%hash;
my $tree = '';
my $level = 0;

while (@stack) {
    my ($hash_ref, $keys_ref);
    if (ref($stack[-1]) eq 'ARRAY') {
        ($hash_ref, $keys_ref) = @{ pop @stack };
        $level--;
    } else {
        $hash_ref = pop @stack;
        $keys_ref = [ sort { $b <=> $a } (keys %$hash_ref) ];
    }
    while (my $key = pop @{ $keys_ref }) {
        my $value = $hash_ref->{$key};
        if (ref($value) eq 'HASH') {
            $tree .= "\t" x $level . "$key => \n";
            push @stack, ([ $hash_ref, $keys_ref ], $value);
            $level++;
            last;
        } else {
            $tree .= "\t" x $level . "$key => $value\n";
#            print "on key $key, stack is ", $level, " levels deep.\n"
+;
        }
    }
}
print $tree;
Replies are listed 'Best First'.
RE: Iterate over HoH without Recursion
by ZZamboni (Curate) on May 21, 2000 at 04:59 UTC
    Very nice piece of code. However, it is actually more expensive than doing it with recursion, because you are really implementing the stack handling that recursion does, but in Perl (instead of C, in which Perl is written).

    Using the code below, I get the following:

    Benchmark: timing 10000 iterations of ZZamboni, chromatic... ZZamboni: 12 wallclock secs ( 5.64 usr + 0.00 sys = 5.64 CPU) chromatic: 19 wallclock secs ( 8.10 usr + 0.01 sys = 8.11 CPU)
    I don't know how to benchmark memory usage of each subroutine, but that would be interesting because for a very large data structure, doing it non-recursively may actually be good in terms of memory use.

    Here's the code:

    #!/usr/bin/perl -w use strict; use Benchmark; my $tree = ''; my %hash = ( 1 => 1, 2 => { 3 => 4, 5 => { 6 => 7 }, }, 8 => 9, 10 => {}, ); sub ZZamboni { my %hash=%{$_[0]}; my $level=$_[1]; my $tree=''; my $key; foreach $key (sort { $a <=> $b } keys %hash) { $tree .= "\t" x $level . "$key => "; if (ref($hash{$key})) { $tree .= "\n" . ZZamboni($hash{$key}, $level+1); } else { $tree .= $hash{$key} . "\n"; } } return $tree; } sub chromatic { my %hash=%{$_[0]}; my @stack; push @stack, \%hash; my $level = 0; my $tree=''; while (@stack) { my ($hash_ref, $keys_ref); if (ref($stack[-1]) eq 'ARRAY') { ($hash_ref, $keys_ref) = @{ pop @stack }; $level--; } else { $hash_ref = pop @stack; $keys_ref = [ sort { $b <=> $a } (keys %$hash_ref) +]; } while (my $key = pop @{ $keys_ref }) { my $value = $hash_ref->{$key}; if (ref($value) eq 'HASH') { $tree .= "\t" x $level . "$key => \n"; push @stack, ([ $hash_ref, $keys_ref ], $va +lue); $level++; last; } else { $tree .= "\t" x $level . "$key => $value\n" +; # print "on key $key, stack is ", $level, " l +evels deep.\n"; } } } return $tree; } timethese(10000, { 'ZZamboni' => sub { ZZamboni(\%hash, 0) }, 'chromatic' => sub { chromatic(\%hash) }, });

    --ZZamboni

      ++ZZamboni. Since I can see myself reusing some variation of this, here is my version of it, which just has some stylistic changes that make it slightly easier to read (for me!).
      sub ZZamboni { my $hash = shift; my $level = shift; $level = $level ? $level : 0; my $tree=''; my $key; foreach $key (sort { $a <=> $b } keys %$hash) { $tree .= "\t" x $level . "$key => " ; if (ref($hash->{$key})) { $tree .= "\n" . ZZamboni($hash->{$key}, $level+1) ; } else { $tree .= $hash->{$key} . "\n"; } } return $tree; }
Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: snippet [id://13940]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others chanting in the Monastery: (4)
As of 2020-04-04 03:20 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    The most amusing oxymoron is:
















    Results (32 votes). Check out past polls.

    Notices?