Beefy Boxes and Bandwidth Generously Provided by pair Networks
Your skill will accomplish
what the force of many cannot
 
PerlMonks  

Checking if hash value exists without implicitly defining it

by Doctrin (Beadle)
on Feb 14, 2013 at 15:20 UTC ( #1018739=perlquestion: print w/ replies, xml ) Need Help??
Doctrin has asked for the wisdom of the Perl Monks concerning the following question:

Hello Monks )) There is a piece of code:
$Hash{44.52}{param1}{key1} = [101.01,'val1']; $Hash{95.01}{param2}{key2} = [101.02,'val2'];
I want to check if $Hash{80.0}{param1}{key1} exists and contains a value so that 80.0 DOES NOT appar in keys %Hash. So I do:
my $neededVal = 80.0; if (defined $Hash{$neededVal}{param1}{key1}) { print "DEFINED"; } else { for (keys %Hash) { print "$_\n"; #this prints 80.0 as a KEY (among the others)!!! How?.. } }
So does anyone know how to check if a hash value by a particular key exists WITHOUT appearing of this key in (keys %Hash) ? Thanks in advance UPD Thanks everyone! :) When I state no autovivification at start - all ok!

Comment on Checking if hash value exists without implicitly defining it
Select or Download Code
Re: Checking if hash value exists without implicitly defining it
by Anonymous Monk on Feb 14, 2013 at 15:26 UTC
Re: Checking if hash value exists without implicitly defining it
by tobyink (Abbot) on Feb 14, 2013 at 15:27 UTC

    The feature where

    exists $hash{a}{b}{c}

    ... will cause $hash{a}{b} to spring into existence is called "autovivification" and is a built-in Perl feature; it's often useful, but not always. (And it's a really annoying word to type!) There's a module on CPAN called autovivification that gives you finer control over autovivification.

    package Cow { use Moo; has name => (is => 'lazy', default => sub { 'Mooington' }) } say Cow->new->name
Re: Checking if hash value exists without implicitly defining it
by Ratazong (Prior) on Feb 14, 2013 at 15:52 UTC

    Hi Doctrin,

    as others have pointed out, you are suffering from autovivification. In order to avoid autovivification in your specific case, you could also check each level of the hash for existence before dereferencing it (and therefore before autovivification is triggered). The code could look like this:

    if (exists $Hash{$neededVal} && exists $Hash{$neededVal}{param1} && ex +ists $Hash{$neededVal}{param1}{key1}) { print "DEFINED\n"; }
    It takes advantage of the fact that the && s inside the condition are evaluated from left to right, and the evaluation is aborted once a "false" occurs.

    HTH, Rata

Re: Checking if hash value exists without implicitly defining it
by Kenosis (Priest) on Feb 14, 2013 at 16:31 UTC

    One option is to short circuit the autovivification:

    use strict; use warnings; my %Hash; $Hash{44.52}{param1}{key1} = [ 101.01, 'val1' ]; $Hash{95.01}{param2}{key2} = [ 101.02, 'val2' ]; my $neededVal = 80.0; if ( exists $Hash{$neededVal} and exists $Hash{$neededVal}{param1} and exists $Hash{$neededVal}{param1}{key1} ) { print "DEFINED"; } else { for ( keys %Hash ) { print "$_\n"; } }

    Output:

    95.01 44.52

    Update I: Added the needed defined, in case of an earlier (for example) $Hash{$neededVal}{param1}{key1} = 0.

    Update II: Replaced defined with exists. Thank you, choroba.

      Huh. I thought you needed to use exists to avoid AVing the hash key you're reading, but that code works.

      This should simplify some of my code.

        Well, it only breaks for the false values like 0 or "" or undef.
        لսႽ ᥲᥒ⚪⟊Ⴙᘓᖇ Ꮅᘓᖇ⎱ Ⴙᥲ𝇋ƙᘓᖇ

        My apologies, as it should be written as:

        ... if ( exists $Hash{$neededVal} and exists $Hash{$neededVal}{param1} and exists $Hash{$neededVal}{param1}{key1} ) { ...

        Will correct the original...

        Update: Replaced defined with exists. Thank you, choroba.

      You don't even need the exists (assuming you don't have a weird freeform data structure):

      if (Hash{$neededVal} and $Hash{$neededVal}{param1} and $Hash{$neededVal}{param1}{key1} > 4) {

        Yes, you're correct! Originally omitted exists when not testing for the final value--then overgeneralized. However, testing $Hash{$neededVal}{param1}{key1} would fail when $Hash{$neededVal}{param1}{key1} == 0, but expliciting testing the value, as you've shown, would remedy that.

Re: Checking if hash value exists without implicitly defining it
by trizen (Friar) on Feb 14, 2013 at 20:18 UTC
    Here is a recursive check, without autovivification.
    sub _exists { my ($hash, @keys, @exists) = @_; if (exists $hash->{$keys[0]}) { my $key = shift @keys; push @exists, 1, _exists($hash->{$key}, @keys); } return @exists; } sub exists_keys { return &_exists == $#_ ? 1 : (); } my %hash; #$hash{a}{b}{c} = undef; if (exists_keys(\%hash, qw(a b c))) { print "Exists.\n"; } else { print "Doesn't exists.\n"; }

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://1018739]
Approved by Ratazong
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others musing on the Monastery: (10)
As of 2014-09-19 10:31 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    How do you remember the number of days in each month?











    Results (135 votes), past polls