Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

I have a trivial project that has turned into something beyond my scope with recursive algorithms in perl. The following script does almost everything that I need, except print the full path to each value.

eaxmple:
3 => 6
should be
II/B/3 => 6

Please note that the final hash is unknown at runtime, and can be of any size and depth. The output format is specific to an old program that we still use, else I would just dump it using Data Dumper.

#!/usr/bin/perl -w use strict; my %hash = ( 'I' => 1, 'II' => { 'A' => 3, 'B' => { '1' => 4, '2' => 5, '3' => 6} }, 'III' => 2 ); print_hash(\%hash); sub print_hash { my($href) = @_; foreach my $key (keys %$href) { if (ref($$href{$key}) eq 'HASH') { my $href2 = $$href{$key}; print_hash($href2); } else { print "$key => $$href{$key}\n"; } } }

Replies are listed 'Best First'.
Re: Printing Hashes (Without Data Dumper)
by Jenda (Abbot) on May 26, 2003 at 12:21 UTC

    Just pass the "path" to the recursive calls to print_hash:

    sub print_hash { my ($href, $path) = @_; foreach my $key (keys %$href) { if (ref($$href{$key}) eq 'HASH') { my $href2 = $$href{$key}; print_hash($href2, (defined $path ? "$path/$key" : $key)); } else { print ((defined $path ? "$path/$key" : $key), " => $$href{$key +}\n"); } } }

    Jenda
    Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live.
       -- Rick Osborne

    Edit by castaway: Closed small tag in signature

Re: Printing Hashes (Without Data Dumper)
by Skeeve (Parson) on May 26, 2003 at 12:27 UTC
    You almost have it. Simply add these small changes:
    print_hash(\%hash,''); # ^^^ sub print_hash { my($href,$pre) = @_; # ^^^^^ foreach my $key (keys %$href) { if (ref($$href{$key}) eq 'HASH') { my $href2 = $$href{$key}; print_hash($href2,$pre.$key.'/'); # ^^^^^^^^^^^^^^
    It's just a matter of letting the "recursed" calls now of the part which ist to $prepend ;-)
Re: Printing Hashes (Without Data Dumper)
by arthas (Hermit) on May 26, 2003 at 12:18 UTC
    Hi! I modified you loop as follows. I addess a @hist array, which works as a stack. The output seems to be exactly what you want.
    my @hist; sub print_hash { my($href) = @_; foreach my $key (keys %$href) { if (ref($$href{$key}) eq 'HASH') { my $href2 = $$href{$key}; push @hist, $key; print_hash($href2); pop @hist; } else { foreach my $h(@hist) { print $h."/"; } print "$key => $$href{$key}\n"; } } }
    Output:
    III => 2 I => 1 II/A => 3 II/B/1 => 4 II/B/3 => 6 II/B/2 => 5
    Michele.
Re: Printing Hashes (Without Data Dumper)
by broquaint (Abbot) on May 26, 2003 at 13:34 UTC
    Now that you've had a bunch of nice recursive solutions here's an iterative one
    my %hash = ( foo => { bar => { baz => 'one', quux => 'two', }, }, field => 'value', this => { that => undef, }, ); my @stack = \%hash; my @path; while(@stack) { my($k,$v); unless( ($k, $v) = each %{ $stack[0] } ) { shift @stack; pop @path; next; } if(ref $v eq 'HASH') { unshift @stack => $v; push @path => $k; } else { print join('/' => @path, $k), " => ", ( defined $v ? $v : "undef" ), "\n"; } } __output__ foo/bar/quux => two foo/bar/baz => one this/that => undef field => value
    Not a recommendation by any means, but just another way to do it. It's essentially a recursive approach unrolled which is done by maintaining your own stack and probably quite handy when you're optimizing recursive algorithms (see. Unrolling recursion for more info).
    HTH

    _________
    broquaint

Re: Printing Hashes (Without Data Dumper)
by Abigail-II (Bishop) on May 26, 2003 at 12:53 UTC
    sub print_hash; sub print_hash { my ($hash, @prefix) = @_; while (my ($key, $value) = each %$hash) { print join "/" => @prefix, $key; print " => "; if (ref $value eq 'HASH') { print "{\n"; print_hash $value, @prefix, $key; print "}\n"; } else { print $value, "\n"; } } }

    Abigail

Re: Printing Hashes (Without Data Dumper)
by BrowserUk (Patriarch) on May 26, 2003 at 14:19 UTC

    #! perl -sw use strict; my %hash = ( I => 1 , II => { A=> 3 , B=> { 1=> 4, 2=> 5, 3=> 6 } } , III=> 2 ); sub hprint{ return "$_[1] => $_[0]\n" unless ref $_[0]; print for map { hprint( $_[0]{$_}, "$_[1]/$_" ) } keys %{ $_[0] }; (); } hprint \%hash, 'hash'; __END__ D:\Perl\test>test hash/II/B/1 => 4 hash/II/B/2 => 5 hash/II/B/3 => 6 hash/II/A => 3 hash/I => 1 hash/III => 2

    Examine what is said, not who speaks.
    "Efficiency is intelligent laziness." -David Dunham
    "When I'm working on a problem, I never think about beauty. I think only how to solve the problem. But when I have finished, if the solution is not beautiful, I know it is wrong." -Richard Buckminster Fuller
Re: Printing Hashes (Without Data Dumper)
by Anonymous Monk on May 26, 2003 at 16:52 UTC
    Beware the recursive data structure. You'll overflow yer stack...