Your skill will accomplishwhat the force of many cannot PerlMonks

### Hashes Question

by anshumangoyal (Scribe)
 on Nov 30, 2012 at 06:13 UTC 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 (Canon) 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();

CountZero

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

Coyote
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 (Monsignor) on Nov 30, 2012 at 17:33 UTC

Regards, Karl

«The Crux of the Biscuit is the Apostrophe»

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

Create A New User
Node Status?
node history
Node Type: perlquestion [id://1006411]
Approved by frozenwithjoy
Front-paged by 2teez
help
Chatterbox?
and the questions are moot...

How do I use this? | Other CB clients
Other Users?
Others rifling through the Monastery: (9)
As of 2018-05-22 07:07 GMT
Sections?
Information?
Find Nodes?
Leftovers?
Voting Booth?
World peace can best be achieved by:

Results (163 votes). Check out past polls.

Notices?