2

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.

Timur Shtatland
  • 12,024
  • 2
  • 30
  • 47
Zhivko Angelov
  • 139
  • 2
  • 7
  • Note that the arrow operator in hashes is optional after the first use, i.e. `$foo->{bar}->{baz}` can be written `$foo->{bar}{baz}`, which is somewhat easier to read. – TLP Aug 20 '20 at 15:47

2 Answers2

3

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;
Dave Cross
  • 68,119
  • 3
  • 51
  • 97
  • 1
    Turning off autovivification to avoid something that is not a problem might cause actual problems. – TLP Aug 20 '20 at 16:09
  • @TLP, By default, in only turns off autovivification in a few circumstances where it's likely to be undesired. You are NOT like to run into problems. And don't forget that it's lexically-scoped, so you can use it only where its needed if you're worried. – ikegami Aug 20 '20 at 17:30
1

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";
}
TLP
  • 66,756
  • 10
  • 92
  • 149