Zhivko Angelov
Zhivko Angelov

Reputation: 139

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.

Upvotes: 2

Views: 778

Answers (2)

TLP
TLP

Reputation: 67918

As Dave Cross pointed out, this is autovivification. Normally, you do not need to worry about it, unless you at some point make assumptions based on the existence of this hash key, or if your data sets are so large as to require very careful memory regulation.

You can use the no autovivification pragma, preferably in a limited lexical scope, such as this:

my $user;
{  # no autovivification is limited to this block
    no autovivification;
    $user = $data->{addon}{$site}{username} // "nothing";
}

// is the defined-or operator, which is convenient to use in this case.

But for a simple problem like this, you might get away with a simpler solution:

my $user;
if ( defined $data->{addon}{$site} ) {
    $user = $data->{addon}{$site}{username} // "nothing";
}

Upvotes: 1

Dave Cross
Dave Cross

Reputation: 69274

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;

Upvotes: 3

Related Questions