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

Hi all, I have a data structure (hash of hashes) that looks like this:
$VAR = { 'A' => { 'B' => { 'C' => {}, 'D' => {} }, 'E' => { 'F' => { 'G' => {}, 'H' => {} }, 'I' => {} } }, 'J' => { 'K' => { 'L' => {}, } } }
I need to turn this into dotted notation.
The following is the representation I am trying to achieve of the structure above:
A A.B A.B.C A.B.D A.E A.E.F A.E.F.G A.E.F.H A.E.I J.K J.K.L
The structure can be arbitrarily deep. Each level can have an arbitrary number of elements.
I have tried a number of recursive solutions but can't seem to get my head wrapped around it.
Any help would be greatly appreciated.

Replies are listed 'Best First'.
Re: walking a hash
by JavaFan (Canon) on May 04, 2010 at 02:18 UTC
    Sounds like homework to me. So, just a minimal solution.
    sub r; sub r {map {my $__ = $_; $_, map {"$__.$_"} r ${$_[0]}{$_}} keys %{$_[ +0]}}
    Call the sub with your hashref.
Re: walking a hash
by GrandFather (Saint) on May 04, 2010 at 05:17 UTC

    So show us what you have tried! How can we fix code we can't see and isn't even described? How can we get your brain pointed in the right direction if you don't show us where it is pointed right now?

    A recursive solution is the easiest solution so you are on the right track. Showing us your 'best' attempt will help a lot to let us see where you are having trouble.

    True laziness is hard work
      Sorry I didn't include the code I have been trying. Here it is. This is the data set I have been using.
      $rh_dtree = { 'L1' => { 'L2b' => { 'L2b1' => {}, 'L2b2' => {} }, 'L2a' => { L3b' => { L4a' => {} }, L3a' => {} } }, 'L3b' => { 'L4a' => {} }, 'L2b' => { 'L2b1' => {}, 'L2b2' => {} }, 'L2a' => { 'L3b' => { 'L4a' => {} }, 'L3a' => {} } };
      I have changed the code many times. Here is my latest attempt.
      Main(); Main { my %dtree = init(); processDtree(\%dtree); } sub processDtree { my $rh_dtree = shift; foreach my $parent ( keys %{$rh_dtree} ) { my @nodenames = (); hash_walk( \%{$rh_dtree->{$parent}},[],\@nodenames); print join("\n",@nodenames) . "\n"; exit; } } sub hash_walk { my ($rh_dtree,$ra_keys,$ra_nodenames) = @_; while (my ($k, $v) = each %$rh_dtree) { # keep keys ordered. push(@$ra_keys,$k); if ( scalar keys %{$v} ) { hash_walk($v,$ra_keys,$ra_nodenames); } } push(@$ra_nodenames, join(".",@$ra_keys)); @$ra_keys = (); }

      The output from running the follows: Note that I am exiting in the first iteration of the foreach loop in processDtree to make it easier to examine the output.

      L2b.L2b1 L2b.L2b1.L2b2 L2b.L2b1.L2b2 L2b.L2b1.L2b2.L2a.L3b.L4a L2b.L2b1.L2b2.L2a.L3b.L4a L2b.L2b1.L2b2.L2a.L3b.L4a.L3a L2b.L2b1.L2b2.L2a.L3b.L4a.L3a
      I would like to achieve this:
      nested-L2b nested-L2b.nested-L2b1 nested-L2b.nested-L2b2 nested-L2a nested-L2a.nested-L3b nested-L2a.nested-L3b.nested-L4a nested-L2a.nested-L3a

        Copy and paste is your friend. Neither the sample data nor the sample code compile. When the obvious errors are fixed the code only generates the first line of output you show. If the exit is taken out the output still isn't anything like the sample output you give. Even then, the output you show is not consistent with the description you gave in the original node. I notice some duplication in your sample hash - is that intentional?

        Show us a self contained script that runs and we may be able to reproduce your results. Anyway, here is my version of something that may relate to what you want:

        #!/usr/bin/perl use strict; use warnings; my %dtree = ( L1 => { L2b => {L2b1 => {}, L2b2 => {}}, L2a => {L3b => {L4a => {}}, L3a => {}} }, L3b => {L4a => {}}, L2b => {L2b1 => {}, L2b2 => {}}, L2a => {L3b => {L4a => {}}, L3a => {}} ); processDtree(\%dtree); sub processDtree { my ($rh_dtree, @nodes) = @_; print join ('.', map {"nested-$_"} @nodes), "\n" if @nodes; processDtree ($rh_dtree->{$_}, @nodes, $_) for sort keys %$rh_dtre +e; }

        Prints:

        nested-L1 nested-L1.nested-L2a nested-L1.nested-L2a.nested-L3a nested-L1.nested-L2a.nested-L3b nested-L1.nested-L2a.nested-L3b.nested-L4a nested-L1.nested-L2b nested-L1.nested-L2b.nested-L2b1 nested-L1.nested-L2b.nested-L2b2 nested-L2a nested-L2a.nested-L3a nested-L2a.nested-L3b nested-L2a.nested-L3b.nested-L4a nested-L2b nested-L2b.nested-L2b1 nested-L2b.nested-L2b2 nested-L3b nested-L3b.nested-L4a
        True laziness is hard work
Re: walking a hash
by ikegami (Patriarch) on May 04, 2010 at 02:33 UTC

    I have tried a number of recursive solutions

    That's definitely the way to go. Dumping a node involves

    • Printing the node's name
    • Dumping each of its children

    To be able to print the node's name, you need the name of its ancestors. You can simply pass them as arguments.