Beefy Boxes and Bandwidth Generously Provided by pair Networks
good chemistry is complicated,
and a little bit messy -LW
 
PerlMonks  

Re: Hashes Question

by Don Coyote (Monk)
on Nov 30, 2012 at 13:59 UTC ( #1006480=note: print w/ replies, xml ) Need Help??


in reply to Hashes Question

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


Comment on Re: Hashes Question
Select or Download Code

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: note [id://1006480]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others exploiting the Monastery: (4)
As of 2014-08-30 07:06 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    The best computer themed movie is:











    Results (291 votes), past polls