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

I've a script which produces the following output:

subnet,prefix,name 10.0.0.0,30,Site ABC 10.0.0.4,30,ISDN dial-up for Ex House 10.0.0.8,30, 10.0.0.12,30,P2P link

Before printing the output, I would like to check if the "name" field is undef and if so, set a value. The data is in a hash, and when I dump out the values, I think this is a hash of hashes:

$VAR1 = { '10.0.0.0,30' => { 'comment' => 'Site ABC', 'ipn' => 167772160, 'maskn' => 4294967292 }, '10.0.0.4,30' => { 'comment' => 'ISDN dial-up for Ex House', 'ipn' => 167772164, 'maskn' => 4294967292 }, '10.0.0.8,30' => { 'comment' => undef, 'ipn' => 167772168, 'maskn' => 4294967292 }, '10.0.0.12,30' => { 'comment' => 'P2P link', 'ipn' => 167772172, 'maskn' => 4294967292 },

So, I think i need to iterate over the outer hash and then check if the value for 'comment' in the inner hash is undef; if it is, then set it. I have struggled to work out how to use the comment key in the inner hash, I came up with the following, but it doesn't work (the data is in %subnets):

foreach my $ip ( keys %subnets ) { if (undef $subnets{$ip}{$comment}) { $subnets{$ip}{$comment} = 'ALLOCATED'; } }

Where did I go wrong?!

Replies are listed 'Best First'.
Re: Check/set hash of hashes value
by davido (Cardinal) on Jun 24, 2013 at 16:09 UTC

    It's not if( undef $subnets{$ip}{$comment} ), it's if( ! defined $subnets{$ip....

    Right now you're setting $subnets{$ip}{$comment} to undef.


    Dave

      Thanks Dave, so let me get this right - here, the value I was attempting to check if it was undef, I was effectively setting the value to undef !

        Just as you wouldn't use "=" to test equality (you would use "=="), you wouldn't use "undef" to test definedness, you would use "defined".

        undef will undefine its operand. defined will test its operand.


        Dave

        Exactly. See undef, which is a wildly different function than defined.

        #11929 First ask yourself `How would I do this without a computer?' Then have the computer do it the same way.

Re: Check/set hash of hashes value
by hdb (Monsignor) on Jun 24, 2013 at 16:18 UTC

    I think you should be using $subnets{$ip}{'comment'} instead of $subnets{$ip}{$comment} unless the variable $comment contains the string 'comment'.

    UPDATE: (before I had seen kennethk's post or he this update:) The whole thing can be expressed as (in newer versions of Perl):

    $subnets{$_}{'comment'} //= 'ALLOCATED' for keys %subnets;

    UPDATE 2: Comment from kennethk: code can also be written as

    $_->{comment} //= 'ALLOCATED' for values %subnets;
Re: Check/set hash of hashes value
by kennethk (Abbot) on Jun 24, 2013 at 16:29 UTC
    ++davido and ++hdb. If you'd like to save yourself a little typing and are using 5.10 or better (probably), you could use Logical Defined Or Assignment Operators to produce what is cleaner and more obvious to me (YMMV):
    foreach my $ip ( keys %subnets ) { $subnets{$ip}{comment} //= 'ALLOCATED'; }

    #11929 First ask yourself `How would I do this without a computer?' Then have the computer do it the same way.

Re: Check/set hash of hashes value
by stroke (Acolyte) on Jun 24, 2013 at 17:41 UTC

    Thanks people - that did the trick ! I've tested out both and they work. But perhaps for me, this is more obvious and will make it easier for me to work out exactly what I'm doing here, should I need to look back on it:

    foreach my $ip ( keys %subnets ) { $subnets{$ip}{comment} //= 'ALLOCATED'; }

    Thanks again

      You can use this way as well..

      foreach $ip (keys(%subnets)) { if("$subnets{$ip}{'comment'}" eq "") { $subnets{$ip}{'comment'} = "ALLOCATED"; } print "$subnets{$ip}{'comment'}\n"; }
        If you are testing if a value is defined, you should test that. It is possible (not knowing this person's dataset) that empty string is a legitimate value. exists, defined and eq are different for a reason.

        If you use warnings, Perl will emit a warning every time you compare with an undefined value; it's a helpful tool to figure out if you've forgotten to initialize a variable. With your code and warnings, there would be a large amount of unnecessary noise on STDERR.


        #11929 First ask yourself `How would I do this without a computer?' Then have the computer do it the same way.