Complex Data Structures

use v5.36;
use Data::Dumper;
$Data::Dumper::Sortkeys = 1;
$Data::Dumper::Indent   = 1;

# Array of arrays (2D matrix)
my @matrix = ([1, 2, 3], [4, 5, 6], [7, 8, 9]);
say $matrix[1][2];                 # 6 (arrows between subscripts are optional)
for my $row (@matrix) { say join ' ', @$row }

# Hash of arrays
my %hobbies = (ana => ['chess', 'go'], bo => ['tennis']);
push @{ $hobbies{ana} }, 'piano';
push $hobbies{cy}->@*, 'swim';     # autovivification creates $hobbies{cy} = []

# Array of hashes
my @users = (
    { name => 'Ana', age => 30, roles => ['admin', 'dev'] },
    { name => 'Bo',  age => 25, roles => ['dev'] },
);
say $users[0]{roles}[1];           # dev
my @admins = grep { grep { $_ eq 'admin' } @{ $_->{roles} } } @users;
say $_->{name} for sort { $a->{age} <=> $b->{age} } @users;

# Hash of hashes
my %config = (
    database => { host => 'localhost', port => 5432 },
    cache    => { host => 'redis',     port => 6379 },
);
for my $service (sort keys %config) {
    say "$service: $config{$service}{host}:$config{$service}{port}";
}

# Deeply nested data (like JSON)
my $data = {
    company => 'Acme',
    departments => [
        { name => 'R&D', employees => [ { name => 'A
…

13.1 Autovivification

Accessing a nested element that doesn't exist creates the intermediate structures automatically:

my %h;
$h{a}{b}{c} = 1;                          # creates $h{a} = {}, $h{a}{b} = {}
if (exists $h{x}{y}) { }                  # side effect: creates $h{x} = {}!
if (exists $h{x} && exists $h{x}{y}) { }  # safe check
no autovivification;                      # CPAN pragma to disable it

13.2 Traversing Arbitrary Structures

sub walk($node, $depth = 0) {
    my $pad = '  ' x $depth;
    if (ref $node eq 'HASH') {
        for my $k (sort keys %$node) { say "$pad$k:"; walk($node->{$k}, $depth + 1) }
    } elsif (ref $node eq 'ARRAY') {
        walk($_, $depth + 1) for @$node;
    } else {
        say $pad, $node // 'undef';
    }
}
walk($data);