Let me start off with a simple minimal example:
use strict;
use warnings;
use Data::Dumper;
my %hash;
$hash{count} = 4;
$hash{elems}[$_] = {} for (1..$hash{count});
print Dumper \%hash;
Here is the result (reformatted):
$VAR1 = {
'count' => 4,
'elems' => [undef, {}, {}, {}, {}]
};
I do not understand, why did the first element of $hash{elems} become an undef?
I know there are probably easier ways to do what I am doing, but I am creating these empty hashes so that I can later do my $e = $hash{elems}[$i] and continue to use $e to interact with the element, eg continue the horror of nested structures with $e->{subelems}[0] = 100.
>Solution :
Array indices start at 0 in Perl (and in most programming languages for that matter).
In the 1st iteration of $hash{elems}[$_] = {} for (1..$hash{count});, $_ is 1, and you thus put {} at index 1 of $hash{elems}.
Since you didn’t put anything at index 0 of $hash{elems}, it contains undef.
To remedy this, you could use push instead of assigning to specific indices:
push @{$hash{elems}}, {} for 1 .. $hash{count};
push adds items at the end of its first argument. Initially, $hash{elems} is empty, so the end is the 1st index (0).
Some tips:
-
The parenthesis are not needed in
for (1..$hash{count}):for 1 .. $hash{count}works just as well and looks a bit lighter. -
You could initialize your hash when you declare it:
my %hash = ( count => 4, elems => [ map { {} } 1 .. 4 ] ); -
Initializing
elemswith an arrayref of hashrefs is often useless, thanks to autovivification. Simply doing$hash{elems}[0]{some_key} = 42will create an arrayref in$hash{elems}, a hashref at index0in this array, containing the keysome_keywith value42.
In some cases though, your initialization could make sense. For instance, if you want to pass$hash{elems}(but not$hash) to a function (same thing if you want to pass$hash{elems}[..]to a function without passing$hash{elems}).