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

This is far outside my level of comfort, and I'm facing a very interesting sitaution. Essentially, I'm dealing with a massive hash. I have access to the hash reference, and I want to traverse the hash and take out the names of the other hashes and their keys. I want to ignore values. Using Data::Dumper is unreadable due to size.

Let me show an example. Note that this is all inside the main hash reference

'flags' => { 'example' => 0, 'font_attributes' => { 'size' => -1, 'colors' => '' }, 'Redacted' => 0, 'Redacted' => [], 'font_changes' => { 'size' => -1, 'colors' => -1 }, 'Redacted' => 0, 'Redacted2' => -1, 'Redacted3' => -1, 'Redacted4' => 0 }, 'Domains' => {}, 'removed_results' => [], 'Words' => {}, 'other_results' => {}, 'recipient_list' => [ 'test@test.com' ], 'Clean' => { 'extra' => {} }, 'result' => 'pass', 'Lists' => { 'Body' => { 'RedactedWordList' => { 'words' => { 'word1' => '5', 'word2' => '1', }, 'words_found' => [], 'weight' => 0 } } }

As you can tell, it just keeps going and going and going and going. I want to be able to print everything except the values. So $startingHash with hashes within it, and those hashes having hashes within them. I want to print the name of every "hash name" and every "key" in a readable format. I can't seem to wrap my brain around how I could do that. I'm tasked with documenting everything available within this mega hash, but I don't need the actual value obviously

Replies are listed 'Best First'.
Re: Traversing a hash of hashes of hashes
by hippo (Archbishop) on Mar 29, 2017 at 08:07 UTC
    I can't seem to wrap my brain around how I could do that.

    First stab: recursion

    #!/usr/bin/env perl use strict; use warnings; use Ref::Util qw/is_hashref/; my $bighr = { foo => 1, bar => { baz => 'quux' } }; my $l = 0; parse_me ($bighr); sub parse_me { my $h = shift; for my $key (keys %$h) { if (is_hashref $h->{$key}) { $l += 4; parse_me ($h->{$key}); $l -= 4; } else { print ' ' x $l . "Key: $key\n"; } } }

    There should be enough in there to get you going.

Re: Traversing a hash of hashes of hashes
by Discipulus (Canon) on Mar 29, 2017 at 08:16 UTC
    Hello kcorj2244 and welcome to the monastery and to the wonderful world of Perl!

    you'll find perldsc an useful read for sure. But the task is not so complex: you can use some usful functions Perl offers to you. For exmple ref can tell you if a value you got fetching some key is a scalar or a hash ref. In the latter case you just to iterate over it as you've already done for the container hash.

    use strict; use warnings; my %h1=('qwqw' => {'qw' => 'qww','df' => 'dfff'},); my %hash=( 'AAA' => 'aaa', 'BBB' => 'bbb', 'CCC' => {'A1' => 'a1','B1' => 'b1','C1' => {'A2' => 'a2','B2' => 'b2' +,'C2' => 'c2END'}}, 'DDD' => 'ddd', 'EEE' => \%h1, 'FFF' => 'sdsfsds', ); ddump (\%hash); sub ddump { my $ref = shift; my $deep = shift||0; foreach my $k (sort keys %{$ref}) { if (ref( ${$ref}{$k})) {print "\t" x $deep."$k =>\n"; &dd +ump (${$ref}{$k}, ($deep+1))} # key and value # else {print "\t" x ($deep)."$k => ${$ref}{$k}\n";} # or just the key as your need else {print "\t" x ($deep)."$k\n";} } } # OUTPUT AAA BBB CCC => A1 B1 C1 => A2 B2 C2 DDD EEE => qwqw => df qw FFF

    L*

    There are no rules, there are no thumbs..
    Reinvent the wheel, then learn The Wheel; may be one day you reinvent one of THE WHEELS.
      Thank you for your response. Is there something special that's required to deference my $bigHash? I believed that %{$bigHash} would suffice, but %bigHash is only populated with the first hash. I don't have direct access to %bigHash just in case you were wondering why I was asking this. I took a look at the perldoc for this, and what I found only resulted in the first hash being in there.
        hello,

        extra braces are genally not needed to do the dereference

        # plain hash note parens() my %plain = (a=>1,b=>2); # an hash reference note braces {} my $ref = {c=>3,d=>4}; #dereferencing example foreach my $k ( keys %$ref ) # is the same of foreach my $k ( keys %{$ref} )

        Generally braces in %{$ref} are not needed because perl knows that there is only one possible way to dereference it.

        For more example see tye's tutorial References quick reference

        L*

        There are no rules, there are no thumbs..
        Reinvent the wheel, then learn The Wheel; may be one day you reinvent one of THE WHEELS.
Re: Traversing a hash of hashes of hashes
by huck (Prior) on Mar 29, 2017 at 15:19 UTC

    This will display hashs in arrays too.

    use strict; use warnings; my $hash={ 'flags' => { 'example' => 0, 'font_attributes' => { 'size' => -1, 'colors' => '' }, 'Redacted' => 0, 'Redacted' => [], 'font_changes' => { 'size' => -1, 'colors' => -1 }, 'Redacted' => 0, 'Redacted2' => -1, 'Redacted3' => -1, 'Redacted4' => 0 }, 'Domains' => {}, 'removed_results' => [1,2,3], 'Words' => {}, 'other_results' => {}, 'recipient_list' => [ 'test@test.com' ], 'Clean' => { 'extra' => {} }, 'result' => 'pass', 'Lists' => { 'Body' => { 'RedactedWordList' => { 'words' => { 'word1' => '5', 'word2' => '1', }, 'words_found' => [], 'weight' => 0 } } } ,'1AoH'=>[{h1=>{a=>1}},{h2=>{a=>2}},{h3=>{a=>3}}] ,'2Aoa'=>[[1,2,3],[1,2,3]] ,'3AoAoH'=>[[{a=>1,2=>3},{a=>1,2=>3},{axx=>{a=>1}}]] ,'4HoHoA'=>{ a11=>[{h111=>{a=>1}},{h211=>{a=>2}},{h311=>{a=>3}},11], a22=>[{h122=>{a=>1}},{h222=>{a=>2}},{h322=>{a=>3}},22], } ,'5HoH'=>{ a411=>{h1111=>{a=>1}, ,h4112=>{h41121=>{a=>2}} ,h4113=>{h41131=>{a=>3}} ,h4114=>1 }, a422=>{h422a=>{h4221=>{a=>1,b=>2} ,a4222=>{h42221=>{a=>2}} ,a4223=>{h42231=>{a=>3}} }, h422b=>1 }, a433=>{a=>1,b=>1,c=>1}, } }; deept($hash,'$hash='); print ';'; sub deept{ my $ref =shift; my $name =shift; my $depth =shift; my $wasfirst=shift; $wasfirst=1 unless (defined($wasfirst)) ; unless ($depth) {$depth=0} else {$depth++} my $depthplus=$depth+length($name); my $first=1; my $lastc=''; my $lines=0; my $sub1=sub { unless ($wasfirst){print ','."\n". (' ' x $depth);} print $name; print $_[0]; $lines++; $lastc=$_[1]; }; my $sub2=sub { my $was=deept($_[0],$_[1],$depthplus,$first); if ($was){ $lines+=$was;$first=0;} }; if (ref($ref) eq "HASH") { $sub1->('{','}'); for my $k (sort keys(%$ref)) {$sub2->($ref->{$k},$k.'=>');} } # hash elsif(ref($ref) eq "ARRAY") { $sub1->('[',']'); for my $m (@$ref) {$sub2->($m,'');} } # array if ($lastc){ if ($lines >2 ){print "\n". (' ' x ($depthplus));} print $lastc; } return $lines; } # deept
    Scalars are not printed, hashs with only scalar values print as {}. arrays with only scalar values print as [].
    $hash={1AoH=>[{h1=>{}}, {h2=>{}}, {h3=>{}} ], 2Aoa=>[[], [] ], 3AoAoH=>[[{}, {}, {axx=>{}} ] ], 4HoHoA=>{a11=>[{h111=>{}}, {h211=>{}}, {h311=>{}} ], a22=>[{h122=>{}}, {h222=>{}}, {h322=>{}} ] }, 5HoH=>{a411=>{h1111=>{}, h4112=>{h41121=>{}}, h4113=>{h41131=>{}} }, a422=>{h422a=>{a4222=>{h42221=>{}}, a4223=>{h42231=>{}}, h4221=>{} } }, a433=>{} }, Clean=>{extra=>{}}, Domains=>{}, Lists=>{Body=>{RedactedWordList=>{words=>{}, words_found=>[] } } }, Words=>{}, flags=>{font_attributes=>{}, font_changes=>{} }, other_results=>{}, recipient_list=>[], removed_results=>[] };

      I might have been a touch confused as to what you were asking. Adding this

      elsif(ref($ref) eq '') { $sub1->('',''); print 'undef'; } # scalar
      before
      if ($lastc){
      prints all scalars as undef.
      $hash={1AoH=>[{h1=>{a=>undef} }, {h2=>{a=>undef} }, {h3=>{a=>undef} } ], 2Aoa=>[[undef, undef, undef ], [undef, undef, undef ] ], 3AoAoH=>[[{2=>undef, a=>undef }, {2=>undef, a=>undef }, {axx=>{a=>undef} } ] ], 4HoHoA=>{a11=>[{h111=>{a=>undef} }, {h211=>{a=>undef} }, {h311=>{a=>undef} }, undef ], a22=>[{h122=>{a=>undef} }, {h222=>{a=>undef} }, {h322=>{a=>undef} }, undef ] }, 5HoH=>{a411=>{h1111=>{a=>undef}, h4112=>{h41121=>{a=>undef} }, h4113=>{h41131=>{a=>undef} }, h4114=>undef }, a422=>{h422a=>{a4222=>{h42221=>{a=>undef} }, a4223=>{h42231=>{a=>undef} }, h4221=>{a=>undef, b=>undef } }, h422b=>undef }, a433=>{a=>undef, b=>undef, c=>undef } }, Clean=>{extra=>{}}, Domains=>{}, Lists=>{Body=>{RedactedWordList=>{weight=>undef, words=>{word1=>undef, word2=>undef }, words_found=>[] } } }, Words=>{}, flags=>{Redacted=>undef, Redacted2=>undef, Redacted3=>undef, Redacted4=>undef, example=>undef, font_attributes=>{colors=>undef, size=>undef }, font_changes=>{colors=>undef, size=>undef } }, other_results=>{}, recipient_list=>[undef], removed_results=>[undef, undef, undef ], result=>undef };

Re: Traversing a hash of hashes of hashes
by Anonymous Monk on Mar 29, 2017 at 08:04 UTC