iterating through hash of hashes

Discussion in 'Perl Misc' started by Jeff Thies, Aug 30, 2003.

  1. Jeff Thies

    Jeff Thies Guest

    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
    Jeff Thies, Aug 30, 2003
    #1
    1. Advertising

  2. "Jeff Thies" <> wrote in message
    news:...
    > 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.
    James E Keenan, Aug 30, 2003
    #2
    1. Advertising

  3. Jeff Thies

    Bob Walton Guest

    Jeff Thies wrote:

    > 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.
    --
    Bob Walton
    Bob Walton, Aug 30, 2003
    #3
  4. Jeff Thies

    Bob Walton Guest

    Bob Walton wrote:

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

    ....


    --
    Bob Walton
    Bob Walton, Aug 30, 2003
    #4
  5. "Jeff Thies" <> wrote in message
    news:...
    >
    > 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
    James E Keenan, Aug 30, 2003
    #5
  6. Jeff Thies

    Jeff Thies Guest

    James E Keenan wrote:
    >

    <snip>
    > >

    > 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

    > 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
    Jeff Thies, Aug 30, 2003
    #6
  7. "Jeff Thies" <> wrote in message
    news:...
    > James E Keenan wrote:
    > > 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.
    >


    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
    James E Keenan, Aug 31, 2003
    #7
  8. Jeff Thies wrote:
    >
    > 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.

    --
    $a=24;split//,240513;s/\B/ => /for@@=qw(ac ab bc ba cb ca
    );{push(@b,$a),($a-=6)^=1 for 2..$a/6x--$|;print "$@[$a%6
    ]\n";((6<=($a-=6))?$a+=$_[$a%6]-$a%6:($a=pop @b))&&redo;}
    Benjamin Goldberg, Sep 2, 2003
    #8
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Steven Hirsch

    Iterating over a hash of hash of hashes

    Steven Hirsch, Aug 19, 2008, in forum: Ruby
    Replies:
    0
    Views:
    149
    Steven Hirsch
    Aug 19, 2008
  2. Scott  Gilpin
    Replies:
    2
    Views:
    216
  3. Perl Learner

    Hashes of hashes or just one hash ?

    Perl Learner, Jun 8, 2005, in forum: Perl Misc
    Replies:
    11
    Views:
    211
  4. Tim O'Donovan

    Hash of hashes, of hashes, of arrays of hashes

    Tim O'Donovan, Oct 27, 2005, in forum: Perl Misc
    Replies:
    5
    Views:
    208
  5. Replies:
    3
    Views:
    205
Loading...

Share This Page