perlkeydefinedhashrefautovivification

perl check nested hash reference


I have the following code:

#!/usr/bin/perl

use warnings;
use strict;
use Data::Dumper;

my $site = "test.com";
my $data = {
        "test" => 1
};

my $user = defined($data->{addons}->{$site}->{username}) ? $data->{addons}->{$site}->{username} : "nothing";

print Dumper($data);

The result:

$VAR1 = {
          'test' => 1,
          'addons' => {
                        'test.com' => {}
                      }
        };

As you can see the check if the user is defined in the nested structure actually creates an empty keys. My questing is how to check the hashref without defining the keys.


Solution

  • You're stumbling over "autovivification". Perl automatically creates intermediate levels in data structures that you try to access. You can see it in action here:

    $ perl -MData::Dumper -E'if (!$foo->{bar}->{baz}){}; say Dumper $foo'
    $VAR1 = {
              'bar' => {}
            };
    

    In order to check whether $foo->{bar}->{baz} was true, Perl created $foo->{bar}. That makes it easy to create complex data structures, but can be problematic when querying them.

    But look at this:

    $ perl -M-autovivification -MData::Dumper -E'if (!$foo->{bar}->{baz}){}; say Dumper $foo'
    $VAR1 = undef;
    

    The autovivification pragma makes it easy to turn off autovivification in parts of your code. So just add:

    no autovivification;
    

    In the block of code that is causing the problems.

    Update: There's also the manual approach, which involves checking each level of the data structure and stopping looking as soon as you find something that doesn't match what you're looking for:

    $ perl -MData::Dumper -E'if ("HASH" eq ref $foo and exists $foo->{bar} and !$foo->{bar}->{baz}){}; say Dumper $foo'
    $VAR1 = undef;