I'm getting the error Can't use an undefined value as an ARRAY reference
in a Perl script. Below is a highly simplified version.
Basically I have setup a hash of arrays where some of them might be empty (B in this case). It works just fine if I DON'T sort the data array. I've tried adding a conditional to test if that particular array exists, but it doesn't like that in this setup. It would not be easy to sort the array as it's populated (unlike in this example).
use strict;
use warnings;
my @list = ('A', 'B', 'C');
my %data_for;
$data_for{'A'} = ['apple', 'astronaut', 'acorn'];
$data_for{'C'} = ['car', 'cook', 'candy'];
# Creates error
foreach my $letter (@list) {
print "$letter: ";
foreach my $item ( sort @{$data_for{$letter}}) {
print "$item, ";
}
print "\n";
}
This is the output I want (seems obvious, but eh):
A: acorn, apple, astronaut,
B:
C: candy, car, cook,
As a strange aside, if I print the working version (without the sort) first, the second version with the sort works without an error. I do not understand this, but I may be able to use that as a work around.
$data_for{B}
is not defined, so the attempt to dereference it as an array (in the sort @{$data_for{$letter}}
expression) is an error.
One workaround is to assign a (empty) value for $data_for{B}
:
$data_for{'A'} = ['apple', 'astronaut', 'acorn'];
$data_for{'B'} = [];
$data_for{'C'} = ['car', 'cook', 'candy'];
but a more general workaround is to use the '||' operator (or '//' operator for Perl >=v5.10) to specify a default value when a hash value is not defined
foreach my $item ( sort @{$data_for{$letter} || []}) { ... }
When $data_for{$letter}
is undefined (and therefore evaluates to false), the expression $data_for{$letter} || []
evaluates to a reference to an empty array, and the array dereference operation will succeed.
So you need to check if $data_for{$letter}
is a reference or not. Possible checks:
exists($data_for{$letter})
.
defined($data_for{$letter})
because a non-existent hash element is undefined.
$data_for{$letter}
because references are always true.
We can use the check to provide us something to dereference:
sort @{ $data_for{$letter} // [] }
We can use the check to avoid dereferencing at all.
sort $data_for{$letter} ? @{ $data_for{$letter} } : ()
This gives us the following:
print "$letter: ";
for my $item ( sort $data_for{$letter} ? @{ $data_for{$letter} } : () ) {
print "$item, ";
}
print "\n";
Or better yet, we can avoid the trailing ,
with the following:
use feature qw( say );
say "$letter: ", join ", ", sort $data_for{$letter} ? @{ $data_for{$letter} } : ();
It works just fine if I DON'T sort the data array.
When you dereference an undefined variable that's used as a modifiable value (lvalue), Perl will automatically create a variable of the appropriate type, and it will place a reference to the newly created variable in the undefined variable. This is called "autovivification".
Since autovivifying only happens in lvalue context, derefencing an undefined value elsewhere leads to the an error.
my ($a,$b); # Both are undefined.
@$a = @b; # OK. Equivalent to @{ $a //= [] } = @b.
@a = @$b; # XXX. "Can't use an undefined value as an ARRAY reference."
sort
doesn't modify its operands, so it doesn't evaluate them in lvalue context.
A foreach loop evaluates its operands in lvalue context to allow for for (@$a) { $_ = uc($_); }
. That means that
for (@{ $data_for{$letter} }) { ... }
implicitly modifies $data_for{$letter}
as follows:
for (@{ $data_for{$letter} //= [] }) { ... }