gw1500se has asked for the wisdom of the Perl Monks concerning the following question:

I am afraid I am not understanding the syntax of hashes, particularly hashes that contain hashes. I am trying to use Mail::Field::Received. In particular the 'parse_tree' object. The documentation says it returns a hashref but the syntax in the example uses '$' rather than '%'. Here is my apparently syntactically incorrect code:
# $string contains the 'Received' header my $received = Mail::Field->new('Received',$string); my $parse_tree = $received->parse_tree(); my $by = $parse_tree{'by'}; my $domain = $by{'domain'};
The keys themselves are hashrefs. So if I wanted to extract the value of the key 'domain' which is part of the key 'by' from the parse_tree, how would I write the correct syntax and when do I use '%' vs. '$'? Thanks.

Replies are listed 'Best First'.
Re: Brief Hash Tutorial
by dreadpiratepeter (Priest) on Aug 31, 2008 at 01:59 UTC
    a hashref is a reference to a hash, so it is actually a scalar that holds the address of a hash. The most common way to get at the hash values is through the use of the arrow operator ->.
    your code would read:
    my $by = $parse_tree->{by};
    Similarly an array ref would be:
    $a->[0];
    etc. Also in the case of nested hashes, you can repeat the process:
    my $domain = $parse_tree->{by}->{domain};
    However, after the initial arrow, further arrows are unnecessary, so:
    my $domain = $parse_tree->{by}{domain}
    will work just as well.


    -pete
    "Worry is like a rocking chair. It gives you something to do, but it doesn't get you anywhere."
      Although I'd recommend using further arrows other wise you'll get your knickers in a twist.

      Remember that % means a whole hash, and $ is a hash value (as it is in effect a scalar).

      So if $hashref contained a hash, then you could access the whole hash with %$hashref (the hash % in the scalar reference $hashref).

      Remember references (or pointers if you will) are a single value (a single reference), so effectively a scalar, hence the $.

      Use arrow notation and the appropriate brackets to access the nested hash/array you need.
      $hashref = \%hash; # $hashref is a scalar pointing to %hash %$hashref or %{$hashref}# the same as %hash $hash{'key'} # is the same as $hashref->{'key'} $hashref->{'key'} = \@array; # Make this key a reference to the array +@array @{$hashref->{'key'}} # the same as @array, the extra brackets {} show +you are looking for the array in $hashref->{'key'} and not an array i +n $hashref $hashref->{'key'}->[0] # same as $array[0]
      It takes a little getting used to, but all makes sense after a while.
      The books Advanced Perl Programming (First Edition) and Intermediate Perl cover this quite well


      Lyle
        Actually, i disagree on the point of keeping the arrows in. Since everything after the first level is always a reference, the extra arrows are just line noise. It's just as easy to remember never to put them, rather than always to.
        Also, that frees you up to concentrate on the real issue of whether your top level access is by reference or not.
        So all the -> rules essentially (for simple to moderate cases) boil down to ref = -> at the top, hash (or list, etc) = no ->
        And as a side note to another point you made, I have started following Damian's advice about the deref and have been writing:
        %{$hashref}
        instead of:
        %$hashref
        it seems a little more readable, especially with a little whitespace:
        %{ $hashref }


        -pete
        "Worry is like a rocking chair. It gives you something to do, but it doesn't get you anywhere."
        Great explanation. I learn best with examples and yours helped a lot.
        So if $hashref contained a hash, then you could access the whole hash with %$hashref (the hash % in the scalar reference $hashref).
        Aside from your advice to leave in all de-referencing arrows, which can be good or bad depending on the context—for example, it can make it clearer to a new user what's going on, but it also completely destroys the often-intended resemblance to a multi-dimensional array (or hash)—I think most of your post is a good description; but you probably didn't say what you meant above. Of course the point is that $hashref doesn't contain a hash (or else it would be %hash or something), but rather that it contains (or perhaps just is) a reference to a hash.
      Thanks for the reply. I see now, I was confusing a hash and hashref. That got me over the first hurdle but now I guess I don't understand Mail::Field as it does not seem to return a 'parse_tree' but rather just a text sting. I need to look for some examples. I might be back.
        According to the docs $received->parse_tree() returns a hashref. The example output shows that it is a hash of hashes (and in the case of comments a HoHoA). With that in mind the below shows how you might loop over such a structure and how to print just the 'domain' value of the 'by' hash.

        You might also want to print your $header string (a better name than $string, by the way) to confirm that it is of a form acceptable to the module.

        #!/usr/local/bin/perl use strict; use warnings; # snippet from docs my $parse_tree = { 'by' => { 'domain' => 'host5.hostingcheck.com', 'whole' => 'by host5.hostingcheck.com', 'comments' => [ '(8.9.3/8.9.3)' ], }, }; my $by = $parse_tree->{q{by}}; for my $key (keys %{$by}){ print qq{$key -> }; if ($key eq q{comments}){ for my $comment (@{$by->{$key}}){ print qq{$comment, }; } print qq{\n}; } else{ print qq{$by->{$key}\n}; } } print qq{\n\n}; print qq{by -> domain: $parse_tree->{q{by}}{q{domain}}\n};
        outputs
        whole -> by host5.hostingcheck.com domain -> host5.hostingcheck.com comments -> (8.9.3/8.9.3), by -> domain: host5.hostingcheck.com
Re: Brief Hash Tutorial
by toolic (Bishop) on Aug 31, 2008 at 02:04 UTC