iterating through hash of hashes

J

Jeff Thies

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
 
J

James E Keenan

Jeff Thies 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);
Before posting code here, you should first try to get Perl to give you all
possible help. Start with:
use strict;
use warnings;
Figure out what's going wrong with your code once you try to run it under
strictures and warnings; then re-post.
 
B

Bob Walton

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.
 
B

Bob Walton

Bob said:
Jeff Thies wrote: ....
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


Oops, it is %A which gets generated in the $h{a}{b}='AB' code,
not %a.

#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';
....
 
J

James E Keenan

Jeff Thies said:
Thanks, and thanks to all. I was clueless about the symbolic reference
error until you explained it.

Perhaps I'm approaching this the wrong way. I have nested data (pseudo
HTML) that I wish to store.

I think as long as I always look at this at the same hash depth I won't
have to worry about creating symbolic references (can I test for those
other than using strict?)

$h{block1}{paragraph}{text}='something';
$h{block1}{paragraph}{id}='1';
$h{block1}{paragraph}{style}='some_style';
$h{block1}{image}{src}='some_source';
$h{block1}{image}{id}='';
$h{block1}{image}{style}='';
Okay, this will run under strict and warnings. For example, with
use Data::Dumper;
....
print Dumper (\%h);

Now, something about which I'm still confused. In your OP, you appeared to
be asking how to *get data out* from a hash of hashes. However, above you
appear to be concerned with data you "wish to store" in such a variable.
While the 2 problems are related, the latter logically precedes the former
and my hunch is that that's what you really want to accomplish, i.e., you
want to avoid having to hard-code the data assignment as in your example
above.

If so, then how you get the data *into* the hash depends mostly on the data
source. If the data were true HTML rather than "pseudo-HTML," people on
this list would be quick to advise your to check out a well-tested CPAN
module such as HTML::parser. But we'd have to know more about your data
source to say for sure.

jimk
 
J

Jeff Thies

James said:
Okay, this will run under strict and warnings. For example, with
use Data::Dumper;
....
print Dumper (\%h);

Now, something about which I'm still confused. In your OP, you appeared to
be asking how to *get data out* from a hash of hashes. However, above you
appear to be concerned with data you "wish to store" in such a variable.
While the 2 problems are related, the latter logically precedes the former
and my hunch is that that's what you really want to accomplish, i.e., you
want to avoid having to hard-code the data assignment as in your example
above.

Yes, it seems that's best as code that's evolving you never know what
the data will eventually look like.
If so, then how you get the data *into* the hash depends mostly on the data
source. If the data were true HTML rather than "pseudo-HTML,"

It's generated by something like this:

http://thelimit.com/cgi-bin/commonsense_edit.pl

That'll require IE5 or NS6 or a DOM2 browser to make sense of the
javascript.

Each of those "blocks" is an object.

So I need to store the object data for each block and the background
data for the whole page.

I started off storing this in a config file with a bunch of crazily
named scalars. Then I switched to a simple hash.

It seems to me that this should probably be stored as xml. But xml is
hard to manipulate unless you load a lot of modules (AnyData from Jeff
Zucker is great). The multi dimension hash seemed like a good idea until
you pointed out the problem of the references and I don't want to
accidentally step on the data.

I'm coming a bit late to writing object orientated perl and I get
pretty confused at times!

Cheers,
Jeff
 
J

James E Keenan

Jeff Thies said:
It's generated by something like this:

http://thelimit.com/cgi-bin/commonsense_edit.pl

That'll require IE5 or NS6 or a DOM2 browser to make sense of the
javascript.

Each of those "blocks" is an object.

An object in the sense of Perl objects (sets of data with methods attached)?
If so, what class are they members of? Or object just in the sense of a
data record (or field therein)?
So I need to store the object data for each block and the background
data for the whole page.

With a certain degree of care it is possible to use a hash to include both.
For example, if you took Bob Walton's suggestion for forming the hash keys
....
$h{block1,paragraph,text}='something';
$h{block2,paragraph,text}='something borrowed';
$h{block3,paragraph,text}='something blue';
.... you then might have a special key which held the background data:
$h{'background'} = [ # an array of background characteristics];

This would require you, when writing accessor methods or subs to exclude
this key from loops:
foreach (keys %h) {
unless ($h{'background'}) {
... # process keys holding data
}
}
I started off storing this in a config file with a bunch of crazily
named scalars. Then I switched to a simple hash.

It seems to me that this should probably be stored as xml.

Depending on the scale of your project and whether your data set will
ultimately have to interact with other data sets.
I'm coming a bit late to writing object orientated perl and I get
pretty confused at times!
For assistance, you already have:
perldoc perlobj
perldoc perlboot
perldoc perltoot
And you can get Randal Schwartz's new book "Perl Objects, References and
Modules" and/or Damian Conway's "Object Oriented Perl."

jimk
 
B

Benjamin Goldberg

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';

Ok so far.
$h{a}{b}='AB';

But this is eqivilant to:

${ "A" }{b} = 'AB';

That is, it uses a symbolic reference. That's bad. Don't do it.

Read the following:

http://perl.plover.com/varvarname.html

After you've fixed your data structure to not muck with perl's symbol
table, you can easily print it out using Data::Dumper.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads

Iterating hashes 11
Sorting hash of hashes 3
hash of hashes 9
Sorting hash of hashes 9
Hash of Hashes 5
Iterating through a hash 12
Sort by number of characters 1
OK to delete hash pairs while iterating through it? 7

Members online

No members online now.

Forum statistics

Threads
474,262
Messages
2,571,056
Members
48,769
Latest member
Clifft

Latest Threads

Top