4. Complex data structures - 1: Array of Arrays


An Array of Arrays is in a sense an extension of a normal array. Instead of scalars as members, it is populated with references to anonymous arrays (1). The array manipulations you've learned in Chapter 4 (Part 1) can be more or less applied to an Array of Arrays: the commands are in fact mere variations. In addition, what you learn in this chapter can be applied to other and more complex data structures.

Recall to construct an anonymous array reference with square brackets: []
$anon_array_ref = ['scalar_1', 'scalar_2', 'scalar_3'];
or
$anon_array_ref = [ qw( scalar_1 scalar_2 scalar_3 ) ];
At the end of this chapter a small discussion on using a reference to an Array of Arrays.


4.1 Create

The declaration of an Array of Arrays:
@AoA = ( # array of arrays; notice the open and close parenthesis: it's a real array: a list assigned to an array variabele!
['red', 'orange', 'yellow'], # first row with an anonymous array
['green', 'blue'], # second row with an anonymous array
);
or

 @AoA = (
          [ qw( red orange yellow ) ],
          [ qw( green blue ) ],
        );
 
To extend an Array of Arrays, use 'push':
push (@AoA, ['purple', 'pink', 'brown']); # add element to @AoA
Creating an Array of Arrays can alternatively be done with successive push commands:
@AoA = (); # empty the Array of Arrays
push (@AoA, ['red', 'orange', 'yellow']);
push (@AoA, ['green', 'blue']);
push (@AoA, ['purple', 'pink', 'brown']);
You can of course convert strings, read e.g. from a text file, and push them to an AoA:
@AoA = (); # empty the Array of Arrays
$str = "red, green, blue";
@split = split(', ', $str);
push @AoA, [ @split ];
4.1.1 Copy
@AoA_copy = @AoA;
4.1.2 Merge
@AoA_combi = (@AoA_1, @AoA_2);
4.1.3 Empty
@AoA = ();


4.2 Access

4.2.1 Access elements via an index
@AoA = (
['red', 'orange', 'yellow'], # first row
['green', 'blue'], # second row
);
print "$AoA[0][1]\n"; # access element via indices [0][1]: first row, second column; output: orange
5.2.2 Index last element
The length is found via its scalar value.
$length_AoA = scalar(@AoA);
$index_last_element_AoA = $length_AoA - 1;
4.2.3 Index last element: alternative
$index_last_element = $#AoA; # the operator '$#' returns the last index of an array and also of an AoA


4.3 Print

foreach $i (0 .. $#AoA) { #  $#AoA is index last element
print "Array $i: @{@AoA[$i]}\n";
}
The 'foreach' loop can be shorter:
foreach $aref (@AoA) {
print "[ @{$aref} ]\n";
}
or (relying on the special variable $_):
foreach (@AoA) {
print "[ @{$_} ]\n";
}


4.4 Iterate

The recommended way for iterating an Array of Arrays is using 'foreach' as shown in 5.1.2.
@AoA_copy = @AoA;
foreach (@AoA_copy) {
print "[ @{$_} ]\n";
}


4.5 Operate

4.5.1 Add a new array or remove an existing array at the end or beginning
As already shown, use 'push' for adding an array at the end:
push (@AoA, ['purple', 'pink', 'brown']);
Use 'unshift' for adding an array at the beginning:
unshift (@AoA, ['purple', 'pink', 'brown']);
The functions 'shift' and 'pop' can be used for respectively removing an array at the end or at the beginning.
pop (@AoA);
shift (@AoA);
4.5.2 Update elements
Select via indices an array (row) and an column (member) and assign a new value:
$AoA[2][1] = 'PINK'
4.5.3 Delete a member: splice and nested loops
The function 'Splice' can be applied to delete specific members:
@AoA = (
['red', 'orange', 'yellow'], # first row
['green', 'blue'], # second row
['purple', 'pink', 'brown'], # third row
);
@AoA_copy = @AoA;
splice (@AoA_copy, 0, 1); # remove the first member, i.e. first array from @AoA_copy
@AoA_copy = @AoA;
splice (@AoA_copy, 0, 2); # remove the first two members from @AoA_copy
@AoA_copy = @AoA;
splice (@AoA_copy, -1, 1); # remove the last member from @AoA_copy
The best way to remove an existing array from the Array of Arrays conditionally is to store matches in a temporary Array of Arrays (AoA_temp). At the end you could copy this AoA_temp to the original AoA:
@AoA = (
['red', 'orange', 'yellow'], # first row
['green', 'blue'], # second row
['black', 'white'], # third row
);
@AoA_temp = ();
foreach my $i (0 .. $#AoA) {
if (! grep (/blue/, @{$AoA[$i]} )) {
push (@AoA_temp, $AoA[$i]);
}
}

@AoA = ();
@AoA = @AoA_temp;
undef @AoA_temp; # forget @AoA_temp ever existed

foreach $aref (@AoA) {
print "[ @{$aref} ]\n"; # output: [ red orange yellow ] and [ black white ]
}
4.5.4 Find members with 'grep'
As already shown, with 'grep' you can find members in an array and in an Array of Arrays:
@AoA_copy = @AoA;
foreach my $i (0 .. $#AoA) {
if (grep {'green' eq $_} @{$AoA_copy[$i]} and grep {'blue' eq $_} @{$AoA_copy[$i]}) {
print "[ $AoA_copy[$i][0] $AoA_copy[$i][1] ]\n";
}
}


Exit: a reference to an Array of Arrays?

If you want to work with a reference to an Array of Arrays, you've to deal with only some more derefencing operators. Be warned! Things can be a bit complicated.

@AoA = ( # array of arrays; notice the open and close parenthesis: it's a real array!
['red', 'orange', 'yellow'], # first row
['green', 'blue'], # second row
);
@AoA returns two references (i.e. internal Perl key) as you'll see if you do 'print ("@A0A");'. In my case:
ARRAY(0x5564530d04b8) ARRAY(0x5564530d0548)
Let's go further:
$AoA_ref = \@AoA; # reference to an Array of Arrays
print "1: $$AoA_ref[1][1]\n"; # access element via index row and column; alternative: $AoA_ref->[1]->[1]

push (@{$AoA_ref}, ['black', 'grey', 'white']); # add element to @AoA via reference
print "2: @{@$AoA_ref[2]}\n"; # print added array member

$$AoA_ref[1][1] = 'BLUE'; # change value of specific element

# print Array of Arrays:

foreach $i (0..$#$AoA_ref) {
print "[ @{@$AoA_ref[$i]} ]\n";
}
or
foreach $aref (@{$AoA_ref}) {
print "[ @{$aref} ]\n";
}
or
foreach (@{$AoA_ref}) {
print "[ @{$_} ]\n";
}
You see: it's all logical but not easier to understand...sometimes extra curly braces may help. Maybe useful for you:
$number_ref_arrays = scalar(@{$AoA_ref}); # output: 3
$last_index_ref_arrays = $#$AoA_ref; # output: 2
$last_index_ref_arrays = $#{$AoA_ref}; # with extra curly braces; output: 2
$no_elements_ref_array = scalar(@{ $AoA_ref->[1] }); # output: 2
or
$no_elements_ref_array = scalar(@{ $$AoA_ref[1] }); # without arrow and with extra $; output also: 2


Footnotes
(1) The common expression Arrays of Arrays is not correct! You should speak of Arrays of References of Arrays.