http://www.perlmonks.org?node_id=1006480


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