Jeff said:
I'd like to iterate through a hash of hashes and get all the values.
How do I do that?
The following works for me. But it seems a bit convoluted and I'm
unable to retrieve where the value came from, only the last key:
my %h;
$h{a}='A';
$h{b}='B';
$h{a}{b}='AB';
$h{a}{c}='AC';
$h{a}{b}{c}='ABC';
$h{a}{b}{a}='ABA';
$h{a}{b}{b}='ABB';
sub getHash{
my($a)=@_;
foreach $key (keys(%$a)){
my %d=%$a;
print qq{<div>$key - $d{$key}</div>};
getHash($d{$key});
}
}
&getHash(\%h);
Cheers,
Jeff
Here is a revamp of your code that works under use strict; -- there are
some very important but perhaps pretty obscure things wrong with your
code, which, as James pointed out, you would have discovered for
yourself if you used strict. I included commentary following lines
with differences from your code:
use strict;
#always during development!
use warnings;
#always during development!
my %h;
$h{a}='A';
$h{b}='B';
$h{aa}{b}='AB';
#a hash key is a scalar value, and thus is (among other
#possibilities) a string or a reference. When you have
#already defined it as a string, as in $h{a}='A' above,
#then a statement such as $h{a}{b}='AB' does not generate
#a reference, but rather a symbolic reference. That means
#it generates a hash %a and stores stuff in that. That
#would be a problem if you had another hash %a somewhere in
#your program and didn't realize that it was being used
#here as well, huh? Or, if it were a real program and the
#string came from some data provided by a user which happens
#to be, well, let's say 'h'. That one should be interesting,
#don't you think? To make things work, I used aa for the key
#here so that $h{aa} will be a genuine reference, not a
#symbolic reference. Ditto for {b} below.
$h{aa}{c}='AC';
$h{aa}{bb}{c}='ABC';
$h{aa}{bb}{a}='ABA';
$h{aa}{bb}{b}='ABB';
sub getHash{
my($a)=shift;
#you were assigning $a to @_, which, since it was a done in
#list context, worked. But a better way is to assign from
#$_[0] or the shift function (which defaults to shifting @_).
#Also, $a and $b are bad variable names to use, because they
#bypass strict (since they are special in sort subs).
my $prev=shift;
#added a second argument to keep track of the previous hash
#keys.
$prev=defined($prev)?$prev.':':'';
#for printing purposes tack on a colon to separate key
#pieces, but only if the previous piece was defined
#(assumes a colon does not appear in your hash keys)
foreach my $key (keys(%$a)){
#you need "my" here to keep the sub successfully
#reentrant (and to pass strict).
#my %d=%$a;
#no need to copy the hash -- just refer to it as below
unless(ref($$a{$key}) eq 'HASH'){
#test to see if a value is a hash reference, print
#it only if it isn't
print qq{<div>$prev$key - $$a{$key}</div>};
}
else{
getHash($$a{$key},$prev.$key);
#call gethash with the previous key pieces and the
#current key piece
}
}
}
getHash(\%h);
#lose the & unless you want the specific things it does
HTH.