Beefy Boxes and Bandwidth Generously Provided by pair Networks
Keep It Simple, Stupid
 
PerlMonks  

re-using a hash reference (was: Mar)

by Anonymous Monk
on Jun 08, 2002 at 15:05 UTC ( [id://172784]=perlquestion: print w/replies, xml ) Need Help??

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

Hi, Basically I want to check a value in one hash and only set a new entry in another hash if its defined in the first i.e.
if ( $hash1->{'a'}->{'b'}->{'c'} ) { $hash2->{'bob'} = $hash1->{'a'}->{'b'}->{'c'}; }
But it strikes me that there must be a more efficient(perlish) way than hitting $hash1 twice? Any views would stop me wasting a lot of time pondering... Cheers Mar

Edit kudra, 2002-06-08 Changed title

Replies are listed 'Best First'.
(jeffa) Re: re-using a hash reference (was: Mar)
by jeffa (Bishop) on Jun 08, 2002 at 15:25 UTC
    Depending upon your needs, sometimes you can get by with just:
    $hash2->{'bob'} = $hash1->{'a'}->{'b'}->{'c'};
    If you check for a defined key named 'bob' in $hash2, you will get a false value - however, if you check for a key named 'bob' that exists in $hash2, you will get a true value. So if you can get by with keys that have no values then don't bother with an if expression. If you can't, then do what vladb suggests (with Juerd's modification, of course).

    Keep in mind that even checking for the presence of a hash key that does not exist will auto-vivify it into existence. Run the following code to see an example:

    use strict; use Data::Dumper; my ($hash1,$hash2); $hash2->{'bob'} = $hash1->{'a'}->{'b'}->{'c'}; print Dumper $hash1, $hash2;

    jeffa

    L-LL-L--L-LL-L--L-LL-L--
    -R--R-RR-R--R-RR-R--R-RR
    B--B--B--B--B--B--B--B--
    H---H---H---H---H---H---
    (the triplet paradiddle with high-hat)
    
      Keep in mind that even checking for the presence of a hash key that does not exist will auto-vivify it into existence.

      Almost :)
      Perl will auto-vivify the hashes/hashkeys *necessary* to evaluate an expression.

      This will clarify the differences:

      perl -MData::Dumper -we 'print Dumper $h1 unless $h1->{a}->{b}->{c}' $VAR1 = { 'a' => { 'b' => {} } };
      In order to test for $h1->{a}->{b}->{c} there must exist a hash for 'c' to be a key in, so Perl auto-vivifies the data structure up to that point. But notice that 'b' points to an empty hash; 'c' is not a key in it! Perl sees that 'c' is not in {'b'}, and that is all that it needs to evaluate the unless.

      perl -MData::Dumper -we 'print Dumper $h1 for $h1->{a}->{b}->{c}' $VAR1 = { 'a' => { 'b' => { 'c' => undef } } };
      Here, we asked Perl to actually *obtain* the value stored in 'c', so Perl went one step further to create the hash key 'c', which holds 'undef', which it returns to the for statement.

Re: Mar
by vladb (Vicar) on Jun 08, 2002 at 15:09 UTC
    Whenever I have to deal with deeply nested hashes, I normally save a reference to a nested hash into a separate 'temporary' variable and work with it instead. Just like here:
    my $hash_b = $hash1->{'a'}->{'b'}; if ($hash_b->{c}) { $hash2->{bob} = $hash_b->{c}; }


    UPDATE: to the editors, I guess someone has to fix the title of this node and making it anything other than 'Mar' ;).

    _____________________
    $"=q;grep;;$,=q"grep";for(`find . -name ".saves*~"`){s;$/;;;/(.*-(\d+) +-.*)$/; $_=["ps -e -o pid | "," $2 | "," -v "," "];`@$_`?{print"+ $1"}:{print" +- $1"}&&`rm $1`; print$\;}
      Normally, what you might do is this:
      if (my $value = $hash1->{a}->{b}->{c}) { $hash2->{bob} = $value; }
      When doing something equivalent to deep sea diving in your nested hashes, it is often easy to keep a pointer to a value or a point in the hash. For example, if you wanted the value of either 'c' or 'd' of the 'b' branch:
      if (my $ref = $hash1->{a}->{b}) { $hash2->{bob} = $ref->{c} || $ref->{d}; }
      You would certainly want to make note of jeffa's remark about autovivification, though. If this structure were persistent, and large, then you would want to carefully test each branch before diving in.

      Whenever I have to deal with deeply nested hashes, I normally save a reference to a nested hash into a separate 'temporary' variable and work with it instead.

      To avoid the reference being there longer than you need, you could wrap it in a block. The array will exist as long as references to it exist.

      - Yes, I reinvent wheels.
      - Spam: Visit eurotraQ.
      

Idiom: Localize with 'for'
by Util (Priest) on Jun 08, 2002 at 19:50 UTC
    A fairly common Perl idiom is:
    # Localize expression into $_ using a single-value 'for'. # $_ is default for many Perl functions. for ($some_very_long_expression_you_will_use_more_than_once) { die unless defined; die unless /^expected/; do_something($_); do_something_else($_); }
    Your code becomes:
    for ( $hash1->{'a'}->{'b'}->{'c'} ) { $hash2->{'bob'} = $_ if defined; }
      I'd use this more often, but it kind of feels strange. Like wearing shoes that are too big, perhaps. You're looping one thing, which means that this is an un-loop. In fact, it's really just a fancy way of making a reference:
      for my $foo ($_[0]->{bar}->{baz}) { if ($foo->{gronk}) { $foo = { glarb => "oorgle" }; } }
      It's a lot easier than making $$foo reference that thing and then constantly dereferencing it.
Re: re-using a hash reference (was: Mar)
by George_Sherston (Vicar) on Jun 08, 2002 at 17:13 UTC
    Or you could put it in a temp variable:
    my $temp; $temp = $hash1->{'a'}->{'b'}->{'c'}; $hash2->{'bob'} = $temp if defined $temp;
    ... which probably saves time digging around in your hash if it's a big'un.

    § George Sherston

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://172784]
Approved by vladb
Front-paged by vladb
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others contemplating the Monastery: (5)
As of 2024-03-28 08:20 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found