Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl Monk, Perl Meditation

Hashes Question

by anshumangoyal (Scribe)
on Nov 30, 2012 at 06:13 UTC ( #1006411=perlquestion: print w/replies, xml ) Need Help??
anshumangoyal has asked for the wisdom of the Perl Monks concerning the following question:

I have 10 subroutines which return Hash References. I call all these subroutines from my main program. Each Subroutine returns different depth level of Hash Reference.

e.g Sub-Routine-1 will return Hash reference of type  $hashRef->{$var1}->{$var2}->{$var3} and Sub-Routine-2 may return Hash reference of type  $hashRef->{$var1}->{$var2} and some of them  $hashRef->{$var1} .

In my main program (Calling program), i dont know what depth of Hash Reference will be returned back. In my main Program I want to accumulate all these hash reference and put them combined in a single hash (Say %MasterHash).

Problem I am facing is iterating the Hash Reference of different depth's which I don't know. Is there any way to do it. I am not pasting my exact code as its a big code. But if some one requires, I will post the same as well.

Replies are listed 'Best First'.
Re: Hashes Question
by NetWallah (Abbot) on Nov 30, 2012 at 07:04 UTC
    Recursion is the key. This StackOverflow article should get you started.

                 "By three methods we may learn wisdom: First, by reflection, which is noblest; Second, by imitation, which is easiest; and third by experience, which is the bitterest."           -Confucius

Re: Hashes Question
by ColonelPanic (Friar) on Nov 30, 2012 at 08:36 UTC

    Here is an example of how you could recurse through a structure of hash references in practice.

    It uses the ref function to determine whether a given level is a hash reference.

    use Modern::Perl; my %ref_chain = ( 'key1' => { 'key2' => { 'key3' => 'not a reference' } }, 'key4' => { 'key5' => 'no ref here' } ); sub traverse_hashes { my $hashref = shift; foreach my $key (keys %$hashref) { if (ref $hashref->{$key} eq 'HASH') { say "$key is a hash ref. Recursing."; traverse_hashes($hashref->{$key}); } else { say "$key is not a hash ref (value: $hashref->{$key})"; } } } traverse_hashes(\%ref_chain);

    When's the last time you used duct tape on a duct? --Larry Wall
Re: Hashes Question
by CountZero (Bishop) on Nov 30, 2012 at 07:08 UTC
    Something like this?
    $MasterHash{'level1'} = sub_return_level_1(); $MasterHash{'level2'} = sub_return_level_2(); $MasterHash{'level3'} = sub_return_level_3();


    A program should be light and agile, its subroutines connected like a string of pearls. The spirit and intent of the program should be retained throughout. There should be neither too little or too much, neither needless loops nor useless variables, neither lack of structure nor overwhelming rigidity." - The Tao of Programming, 4.1 - Geoffrey James

    My blog: Imperial Deltronics
Re: Hashes Question
by Don Coyote (Pilgrim) on Nov 30, 2012 at 13:59 UTC

    Set up a recursive routine to assign existing values of a hash reference to the existing keys of the hashref. Recursion happens when you find one of the values is itself a hashref, when the routine is called on itself again. You may need to check for duplicated values across the different depths though. This is untested but, you get the idea.

    my $newhashref = {}; sub rechash { my $href = shift; foreach (keys %$href){ &rechash($href->{$_}) if ( ref($href->{$_}) && ref($href->{$_}) eq +'HASH'); $newhashref->{$_} = $href->{$_}; #update added '->' dereferencing. } return $newhashref; } my $ref_to_onelevelhash = rechash $ref_to_multilevelhash;

    update after testing:

    There is a slight expense with this solution. Where the value is a hash reference, after recursing, the reference to the hash will then be copied as a new value to the key of the same name. To extract each key and (actual)value pair the foreach if conditional needs to become a full expression, including the post recursion assignment. By which I mean...

    #! \usr\bin\perl use strict; use warnings; my $mlh = my $multilevelhash = {}; $mlh->{spiral} = 'staircase'; $mlh->{polymorph}{psyche} = { dream => 'Coyote', daydream => 'Eagle'}; $mlh->{polymorph}{physic} = { solid => 'liquid', liquid => 'gas'}; #$mlh->{polymorph}{psyche}{meditation} = { daydream => 'Coyote', omnip +otent => 'all hinges'}; ## uh oh duplicate keys at different levels. +.. # my $refnewhas = {}; # see 'update' - switch comments my $refnewhash = {}; sub rechash { #my $refnewhash = $refnewhas; # see 'update' my $href = shift; foreach(keys %$href){ if (ref($href->{$_}) && ref($href->{$_}) eq 'HASH'){ &rechash($href->{$_}) }else{ $refnewhash->{$_} = $href->{$_}; } } # return $refnewhash; # see update - switch comments return; } # my $unihash = rechash $mlh; # update - switch rechash $mlh; # update - switch foreach #foreach(keys %$unihash){ # print "\n",$_," is ",$unihash->{$_}; #}; foreach(keys %$refnewhash){ print "\n",$_," is ",$refnewhash->{$_}; };

    However if you just need to strip out keys that hold hash references into a single level hash, use the first if consruct. The actual values will also be flattened. Notice also, that this sub does not need to specify the return reference. But does need the return statement.

    Update: I am troubled by returning a variable initially declared outside the sub. In attempting to resolve the return value, I couldn't set up the newhashref inside the sub as it is recursive and would be clobbered through each pass. If I set the value to an our lexical it will work, but not by local. The simplest solution that works and contains the reference to be passed within the routine is to 'uncomment' the code. But its starting to get a little messy now... EOU

    update solved. $refnewhash is in scope within the sub, so it does not need to be passed or copied in. And no return statement is required. $refnewhash has been loaded by the subroutine. However return statments depend on the calling context. If you are assigning to $unihash you will need to return a scalar reference such as return $refnewash or return \%$refnewhash. This in turn is a little redundant (seeing as we are not modifiying $href) as it is effectively the same as copying a reference of a loaded $refnewhash to $unihash after calling rechash $mlh. EOU

Re: Hashes Question
by jandrew (Chaplain) on Nov 30, 2012 at 23:36 UTC

    As one of the potential (and newer) 'Walk' module authors you might find on CPAN this a trimmed Synopsis for Data::Walk::Graft

    It is still in development but the API shouldn't change substantially. Feel free to use the module or leverage the source to your preference.

Re: Hashes Question
by karlgoethebier (Parson) on Nov 30, 2012 at 17:33 UTC

    I found something interesting about this at stackoverflow.

    Regards, Karl

    «The Crux of the Biscuit is the Apostrophe»

Re: Hashes Question
by Anonymous Monk on Nov 30, 2012 at 20:25 UTC

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://1006411]
Approved by frozenwithjoy
Front-paged by 2teez
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others pondering the Monastery: (4)
As of 2017-02-27 05:12 GMT
Find Nodes?
    Voting Booth?
    Before electricity was invented, what was the Electric Eel called?

    Results (376 votes). Check out past polls.