Nested data structures
| Array of hashes | |
| Hash of arrays | |
| Walk nested data | |
| Inspect with Data::Dumper | |
| Shallow and deep copy |
Array of hashes
Records usually become an array of hash references: the array keeps the order, and each hash holds the fields of one row. The arrow between the two indexes is the only new syntax you need.
my @users = (
{ name => "Ada", age => 36 },
{ name => "Grace", age => 45 },
);
print $users[1]{name}, "\n"; # Grace
perl demo.pl
Grace
Between two subscripts the arrow may be omitted, so $users[1]{name} is the same as $users[1]->{name}. Keep the arrow when a reference is involved and the reader will thank you.
Hash of arrays
The mirror shape groups several values under one key. A hash of array references is the natural way to store, say, all log lines per host.
my %by_host = (
db1 => ["ok", "ok"],
db2 => ["fail"],
);
push @{$by_host{db2}}, "ok";
print @{$by_host{db2}}, "\n"; # failok, print adds no separator
perl demo.pl
failok
The braces around $by_host{db2} tell Perl where the key ends before it dereferences the array. Writing @$by_host{db2} would mean something else entirely, a hash slice.
Walk nested data
Two nested loops read any array of hashes. Name the row variable and the fields stay obvious even in a long block.
my @users = ({ name => "Ada", age => 36 }, { name => "Grace", age => 45 });
my %by_host = (db1 => ["ok", "ok"], db2 => ["fail", "ok"]);
for my $user (@users) {
print "$user->{name} is $user->{age}\n";
}
for my $host (sort keys %by_host) {
print "$host: ", scalar @{$by_host{$host}}, "\n";
}
perl demo.pl
Ada is 36
Grace is 45
db1: 2
db2: 2
Inspect with Data::Dumper
Printing a reference shows something like HASH(0x55d1c8), which tells you nothing. Data::Dumper is in the core, so it is always available, and it prints the whole structure as Perl source.
my @users = ({ name => "Ada", age => 36 }, { name => "Grace", age => 45 });
use Data::Dumper;
$Data::Dumper::Sortkeys = 1;
print Dumper(\@users);
perl demo.pl
$VAR1 = [
{
'age' => 36,
'name' => 'Ada'
},
{
'age' => 45,
'name' => 'Grace'
}
];
$Data::Dumper::Sortkeys = 1; fixes the key order, because a hash hands out its keys in a different order on every run. Leave it out and the same script prints the fields of each record in a new sequence each time.
Shallow and deep copy
Assigning a reference copies the pointer, not the data: both names then change the same structure. Copy the top level with [@list] or {%hash}, and use Storable for a full deep copy.
my @users = ({ name => "Ada", age => 36 }, { name => "Grace", age => 45 });
use Storable qw(dclone);
my $copy = dclone(\@users);
$copy->[0]{age} = 99;
print $users[0]{age}, "\n"; # 36, original untouched
perl demo.pl
36
Shallow copies are enough when the nested references are never modified. Reach for dclone the moment you plan to write into a structure that someone else also holds.
Next: Regular expressions: match and anchors
Article author: Arthur Isaev