Search code examples
perlhashtableexistsautovivification

Efficiently get hash entry only if it exists in Perl


I am quite often writing fragments of code like this:

if (exists $myHash->{$key}) {
    $value = $myHash->{$key};
}

What I am trying to do is get the value from the hash if the hash has that key in it, and at the same time I want to avoid autovivifying the hash entry if it did not already exist.

However it strikes me that this is quite inefficient: I am doing a hash lookup to find out if a key exists, and then if it did exist I am doing another hash lookup of the same key to extract it.

It gets even more inefficient in a multilevel structure:

if (exists $myHash->{$key1} 
    && exists $myHash->{$key1}{$key2} 
    && exists $myHash->{$key1}{$key2}{$key3}) {

    $value = $myHash->{$key1}{$key2}{$key3};
}

Here I am presumably doing 9 hash lookups instead of 3!

Is perl smart enough to optimize this kind of case? Or is there some other idiom to get the value of a hash without either autovivifying the entry or doing two successive lookups?

I am aware of the autovivification module, but if possible I am looking for a solution that does not require an XS module to be installed. Also I have not had a chance to try this module out and I am not completely sure what happens in the case of a multilevel hash - the pod says that this:

$h->{$key}

would return undef if the key did not exist - does that mean that this:

$h->{$key1}{$key2}

would die if $key1 did not exist, on the grounds that I am trying to de-reference undef? If so, to avoid that presumably you would still need to do multi-level tests for existence.


Solution

  • I wouldn't worry about optimization since hash lookups are fast. But for your first case, you can do:

    if (my $v = $hash{$key}) {
        print "have $key => $v\n";
    }
    

    Similarly:

    if ( ($v = $hash{key1}) && ($v = $v->{key2}) ) { 
        print "Got $v\n";
    }