How to deal with arrays of arrays - Part 1

The following example shows you how to generate an array of arrays using a Perl while loop.

#!/usr/local/bin/perl
 
use warnings;
use strict;
 
my @AoA;
while(<DATA>) {
 chomp;
 push @AoA, [split];
}
 
foreach my $item1 ( @AoA ){
 foreach my $item2 ( @{ $item1 } ){
   print "$item2 "; 
 }
 print "\n";
}
 
__DATA__
25 49 33 200
145 32
11 121 78

In order to read the elements of the @AoA array, I used the __DATA__ pseudo file handle. The while loop will read from <DATA> handle all the lines up to EOF. Inside the while loop:

 

  • each line is assigned in turn to $_
  • the chomp function will remove the trailing newline from $_
  • the split function will split the content of the line stored in $_ into an anonymous array using the whitespace delimiter; [] is the array constructor and it will return a reference to the anonymous array
  • the push function will append this reference to the @AoA array

Finally, nested foreach loops are used to print the Perl @AoA array.

The output produced by this code is as follows:

 

25 49 33 200
145 32
11 121 78

The following example shows you how to get a bi-dimensional array (array of arrays) by using the map function.

See the code:

#!/usr/local/bin/perl
 
use strict;
use warnings;
 
my @AoA = map {[$_, (stat($_))[7]]} glob('*');
 
# print the @AoA array
foreach my $item1 (@AoA){
 foreach my $item2 (@{$item1}){
   print "$item2 "; 
 }
 print "\n";
}

@AoA is an array that has as elements references to other arrays. Each inner array will contain two elements only: the name of a file in the current directory and its size in bytes.

I'll comment this code below:

  • the glob function is called in a list context with an wildcard asterisk character as argument, so it returns a list with all the filenames from the current directory
  • each element of the list returned by glob is assigned in turn to $_
  • {} are the map block curly brackets
  • [] makes a new anonymous array and returns a reference to it:
    • the first element of the anonymous array is the value stored in $_ (i.e. the filename)
    • the second element is the size in bytes of the file – this is obtained by calling (stat($_))[7] where $_ is the name of the file; the stat function returns a 13-element list, and the element with the index 7 means the size of the file in bytes


The 
map function will return the references of the inner arrays in the @AoA array.

Finally, a nested Perl foreach is used to print the Perl @AoA array. Here’s an output sample from my Windows machine:

 cities.txt 144
Colors1 18
Colors2 25
customers.txt 115

A csv file is a comma delimited text data file and ".csv" is its extension.

An example of such a file is shown below:

Persons.csv

 

John,Silva,25,blue

Mary,Brown,32,brown

Paul,Williams,52,green

 

where the raw structure is as follows:

firstname,lastname,age,eyecolor

We intend to read this file and create a bi-dimensional Perl array @AoA with the records of the file.

Each element of the array is a reference to an inner array that has as elements the items from the raw file.

Here's the code:

#!/usr/local/bin/perl
 
use strict;
use warnings;
 
open FILE, "persons.csv" or die $!;
 
my @AoA = map { chomp; [split /,/]; } <FILE>;
 
# print the @AoA array
foreach my $item1 (@AoA){
 foreach my $item2 (@{$item1}){
   print "$item2 "; 
 }
 print "\n";
}

The output is as follows:

John Silva 25 blue
Mary Brown 32 brown
Paul Williams 52 green
 
In this example the map function is used with a block syntax.

Because the map function provides a list context, the file will be read once in a list of records.

Inside the map block:

  • each record from the list will be assigned in turn to $_
  • the chomp function will remove the trailing newline from $_
  • the split function will split the record of the file using the ',' delimiter, returning an anonymous array having as elements the fields of the record
  • [] returns a reference to the new anonymous array returned by the split function

The map function returns the list with the array references in the @AoA array.

Finally, a nested foreach is used o print the @AoA array.

To run this script, you need to create the csv file yourself and put it in your current directory from where you run the script.

The following example shows you how to read a matrix from a file into an Perl array of arrays (@AoA) by using the push function. In this example the file we want to read is 'matrix.txt' and it is located in the same directory as our script. The file contains the elements of a matrix with 3 rows and 4 columns, delimited by whitespace:

112 02 34 45
29 22 89 0123
11 356 -72 321

Here is the code:

#!/usr/local/bin/perl
 
use strict;
use warnings;
 
my @AoA = ();
open my $IN, 'matrix.txt' or die "Could not open file\n";
 
while (defined (my $line = <$IN>)) {
 push(@AoA, [split(' ', $line)]);
}
 
# print the @AoA
print map "@$_\n", @AoA;
 
This code produces the following output:
 
112 02 34 45
29 22 89 0123
11 356 -72 321

The file is read line by line, using a while loop. Each line is stored in turn in the $line iterator variable.

Inside the while loop:

  • the content of $line is split into an anonymous array and the [] array constructor returns a reference to this array; please note the using of the special space character ' ' as pattern in the split function – it will split on whitespace
  • the push function appends the reference to the Perl @AoA array

Finally, the array of arrays is printed using the map function.

The following example shows you how to print the elements of an array of arrays using the for loop. Don’t mix for with foreach, even if sometimes you spell foreach as for.

#!/usr/local/bin/perl
 
use warnings;
use strict;
 
# define an array of arrays
my @AoA = (
 [25, 49, 33, 200],
 [145, 32],
 [11, 121, 78]
);
 
# print the @AoA array 
 
for ( my $i = 0; $i <= $#AoA; $i++ ) {
 for( my $j = 0; $j <= $#{$AoA[$i]}; $j++ ) {
   print "$AoA[$i][$j] ";
 } 
 print "\n";
} 

The [] is the array constructor and returns a reference to the anonymous array (or list) whose elements are included between the square brackets. In our example, [25, 49, 33, 200] returns a reference to the list (25, 49, 33, 200), so our Perl @AoA array has three scalar elements that are respectively references to the following lists: (25, 49, 33, 200), (145, 32), (11, 121, 78).

To print the elements of the @AoA array, we used two nested for.

The outer for is looping across the elements of the array, which are references to other arrays. Here $#AoA is the index of the last element of the @AoA array. To iterate through the indices of the @AoA array, the $i scalar variable is used.

In the inner for, we used the $j scalar variable to loop over the elements of each inner array. The $#{$AoA[$i]} notations means the last index of the inner array that corresponds to the $i index of the @AoA array. $AoA[$i][$j] means the element of index $i and $j where:


  • $i is the index of the main array and
  • $j is the index of the inner array referenced by the value stored in the element of the main array that has the index $i.

You’ll get as output:

25 49 33 200
145 32
11 121 78

The following example shows you how to print the elements of an array of arrays using the foreach loop.

To make the things easier to follow, I used the same @AoA array as before.

#!/usr/local/bin/perl
 
use warnings;
use strict;
 
# define an array of arrays
my @AoA = (
 [25, 49, 33, 200],
 [145, 32],
 [11, 121, 78]
);
 
# print the @AoA array 
 
foreach my $item1 ( @AoA ){
 foreach my $item2 ( @{ $item1 } ){
   print "$item2 "; 
 }
 print "\n";
}

The [] is the array constructor and returns a reference to the anonymous array (or list) whose elements are included between the square brackets.

To print the elements of the @AoA array, we used two nested foreach.

In the outer foreach, the $item1 variable iterator is assigned in turn to the elements of the @AoA array, so it contains a reference to a list.

In the inner foreach, we used the @{$item1} notation to dereference the $item1 reference. The $item2 iterator is assigned in turn to the elements of the inner array referenced by $item1.

Each line will be printed on a separated line, as you can notice from the output of the script:

25 49 33 200
145 32
11 121 78

The following example shows you how to traverse an array of arrays using the grep function. In the example below we use grep to filter out the empty inner arrays.

See the code:

#!/usr/local/bin/perl
 
use warnings;
use strict;
 
my @AoA=(
 [qw(red white)],
 [], 
 [qw(yellow blue green)],
 [],
 [qw(cyan magenta brown)]
);
 
@AoA = grep @$_, @AoA;
print map {"@$_\n"} @AoA;

First, the @AoA Perl array of arrays is populated with a few entries. The [] is the array constructor and returns a reference to the anonymous array (or list) whose elements are included between the square brackets. To avoid quoting the elements of the inner arrays, the qw operator (or function if you want) was used.

The grep function goes through the @AoA array of arrays. At each iteration step the current element of @AoA is assigned in turn to $_. Because $_ contains a reference to an anonymous array, you need to dereference it, prefixing with the @ sign.

The expression @$_ only evaluates to true if it contains one or more entries. In this case the current array reference (stored in $_) is returned by grep. Finally, the Perl @AoA array will contain non empty references only.

If you only need to print the array, you can rewrite the last two code lines on a single line:

print map {"@$_\n"} grep @$_, @AoA;

The map function is used to print the elements of the array of arrays. The "@$_\n" expression means a string containing all the elements of the inner array pointed by $_. This string is ended in a newline character.

In Perl any string that is built with double quotes will be interpolated. When you print an array inside double quotes, the array elements are printed with spaces between (or whatever you have in $").

This code produces the following result:

red white
yellow blue green
cyan magenta brown

The following example shows you how to print the elements of a Perl @AoA (array of arrays) by using the map and print functions. As you know, an array of arrays is an array that has as elements references to other arrays. When the inner arrays have the same size, the array of arrays can be assimilated with a matrix and a matrix raw corresponds to a reference to an inner array.

The following code snippet is a short example, where the Perl array of arrays can be assimilated with a matrix, but this code works for any array of arrays:

#!/usr/local/bin/perl
 
use strict;
use warnings;
 
my @AoA = (
 ['a1', 'b1', 'c1'],
 ['a2', 'b2', 'c2'],
);
 
print map "@$_\n", @AoA;

The output is as follows:

a1 b1 c1
a2 b2 c2

The [] is the array constructor and returns a reference to the list enclosed in square brackets.

To print the @AoA array, the map function was used. Please remember that map is just a foreach in disguise, so you are still looping. So each element of the @AoA array (which is a reference to an inner array) is assigned in turn to $_ and the notation @$_ is used to dereference the reference stored in $_.

The "@$_\n" notation means a string containing all the elements of the array pointed by $_. This string is ended in a newline character. In Perl any string that is built with double quotes will be interpolated. When you print an array inside double quotes, the array elements are printed with spaces between (or whatever you have in $").

If you don’t use interpolation here and rewrite the last statement as:

print map @$_, @AoA;

you’ll get as output:

a1b1c1a2b2c2

i.e. all the elements of all inner arrays.

The following example shows you how to print the elements of an array of arrays using the while loop. It will be printed only the inner arrays elements that are defined.

See the following code snippet:

#!/usr/local/bin/perl
 
use warnings;
use strict;
 
my @AoA = (
 [1, 2, undef, 3],
 [4, 5, 6, undef],
 [undef, 7, undef, 8, 9],
); 
 
my $i=0;
while($i <= $#AoA) {
 my $j=0;
 while ($j <= $#{$AoA[$i]}) {
   print "$AoA[$i][$j]\t" if defined $AoA[$i][$j];
   $j++;
 }
 print "\n";
 $i++;
}

The [] is the array constructor and returns a reference to the list enclosed in square brackets.

We used two nested while loops. The outer while loop iterates through the indices of the Perl @AoA array, using the $i scalar variable. The inner while loop iterates through the indices of the inner arrays, using the $j scalar variables. Both $i and $j variables are initialized with 0 before the first while iteration, being incremented at the end of current iteration.

The $#AoA means the last index of the @AoA array while $#{$AoA[$i]} means the last index of the inner array of index $i.

The defined function is used to get rid of the undefined elements of inner arrays.

The output produced by this code is as follows:

1 2 3
4 5 6
7 8 9

Let’s say that you have a Perl array of arrays (@AoA) and you want to access or modify the elements of a particular inner array. To make the things easier to follow, I’ll illustrate this on a simple array of arrays like the following one:

my @AoA = (
 [12, 25, 27],
 [56, 19]
);
 
The elements of the Perl @AoA array of arrays are references to other arrays. To return a reference to an array, the [] array constructor was used.

In this example we have 2 inner arrays, to get a reference to the first inner array (of index 0), you can write:

my $ref = $AoA[0];
 
To access the second element (of index 1) of this inner array and assign to it a value, you can use either of the two syntax forms:
 
$ref->[1] = 44;
$$ref[1] = 44;

To make things easier, you don’t need an extra variable to store the reference, but use one of the following statements:

$AoA[0]->[0] = 44;
$AoA[0][0] = 44;
 
So, to return an element of index $i$j you can use the syntax $AoA[$i][$j] where $i is an index of the main array and $j is an index of the corresponding inner array.

For the particular case when the array of arrays represent a matrix, $i is the row of the matrix and $j the column (both $i and $j start with 0).