How to deal with arrays of arrays - Part 2

I’ll examine below the array length, by which I mean the length in bytes of an array. I’ll examine the case of array of arrays here.

For computing the array length I’ll use the length function which returns the number of characters of an expression.

If you’ll use Unicode characters in your arrays, the number of characters may be different from the number of bytes.

Let’s examine the following example:

#!/usr/bin/perl
 
use warnings;
use strict;
 
my @AoA = (
 ["blue", "red", "green"],
 ["lightgreen", "lightblue"]
);
 
# using the length() function
{
 use bytes;
 my $count = 0;
 
 foreach my $item1 (@AoA){
   foreach my $item2 (@{$item1}){
     $count += length($item2);
   }
 }
 print "Perl array length (through length() function) is: ",
 $count, "\n";
}

The @AoA is a 2-dimensional array and the code snippet shown above will get us the length in bytes of the @AoA array. I used the use bytes pragma, and I put the snippet code in a block {} in order to limit the effect of this pragma to the scope in which it appears, i.e. inside the block.

To print the length of the Perl @AoA array two nested foreach loops are used.

The output after running this code is as follows:

Perl array length (through length() function) is: 31

This is one way of finding out how big the array is in bytes, but this is not the real memory usage of the array Perl variable. If you want to find the memory usage of an array variable, you can use the Devel::Size module (a quick search through the CPAN modules will help you to find it). However, keep in mind that the result will be dependent on your OS.

The following example shows you how to find a specific string/substring in all the inner arrays of a Perl array of arrays (@AoA).

Please have a look at the following code snippet where we try to match a word in all the elements of the inner arrays:

#!/usr/local/bin/perl
 
use strict;
use warnings;
 
my @AoA = (
 [ qw(Yellow Blue LightBlue DeepPink) ],
 [ qw(DarkGreen DarkMagenta LightYellow) ],
 [ 'Red', 'LightCyan', 'LightCoral', 'Green']
);
 
my @result = grep { $_ =~ /light/i} map { @$_ } @AoA;
print "@result\n";

This script produces the following output:

LightBlue LightYellow LightCyan LightCoral
 
As you can see, first we populate the Perl @AoA array of arrays with three elements. Each element of the @AoA array is a reference to an anonymous array – to create the anonymous array we used the [] array constructor.

For the first two elements I used the qw operator, to remind you how to use it (the qw (quote word) operator helps you avoid writing too many quotation marks).

To loop through the @AoA array, the map function was used. At each iteration step, the current element (which is a reference to an anonymous array) of the @AoA array is assigned in turn to $_.

Inside the map block, the notation @$_is used to dereference the array reference stored in $_. The map function will return to grep a list with all the elements of all inner arrays.

The grep function will loop through the list returned by map, assigned in turn each element of the list to $_.

Next we use the match operator to filter out the elements that not match the word light.

To make the search case insensitive, the match operator (//) was used with the i modifier.

Another way to do this is to use the interpolation "@$_" to transform the inner array into a string (having the array elements separated by space) and next to apply the matching, as you can see in the following line of code:

my @result = grep { "@$_" =~ /green/i } @AoA;
It’s worth noting that in this case the @result array will contain the references of the inner arrays where the match was found.

You will be presented with the same result as before if you’ll use the ~~ smart match operator:

my @result = grep { $_ ~~ /green/i} @AoA;

To join into a string the elements of an inner array of the @AoA Perl array of arrays, you can use the following syntax form:

$string = join EXPR, @ { $AoA [ $index ] };

where $index is the index or subscript of the given inner array.

$AoA [$index] is a reference to an inner array, so you need to dereference it by putting in front a @ sign; to avoid any ambiguity or operators precedence rule, the reference is enclosed by braces.

Here is an example:

use strict;
use warnings;
 
#!/usr/local/bin/perl
 
use strict;
use warnings;
 
my @AoA = (
 ['a1', 'b1', 1],
 ['a2', 'b2', 2],
 ['a3', 'b3', 3],
 ['a4', 'b4', 4],
);
 
my $index = 1; # the second inner array
 
my $str = join ',', @ { $AoA [ $index ]} ;
 
print "$str\n";
# it prints: a2,b2,2

The [] is the array constructor and returns a reference to the list enclosed in square brackets. To join the elements of the inner array, the comma character was used.

If you have a matrix represented as a Perl array of arrays (@AoA), than you can extract the elements of a particular column and join them into a string, by using map and join:

$str = join EXPR, map $_->[$index], @AoA;

where $index is the index of the matrix column.

@AoA contains array references to the matrix rows, the map function will extract for each reference assigned to $_ the corresponding element of the $index column.

Because is more easier to read, for dereferencing the arrow operator (->) was used (if you prefer you can use also the notation $$_[$index]).

The join function will join the elements of the list returned by map into a string, separating them by EXPR.

Here is an example:

#!/usr/local/bin/perl
 
use strict;
use warnings;
 
my @AoA = (
 ['a1', 'b1', 1],
 ['a2', 'b2', 2],
 ['a3', 'b3', 3],
 ['a4', 'b4', 4],
);
 
my $index = 1; # the second column of the matrix
 
my $str = join ',', map $_->[$index], @AoA;
 
print "$str\n";
# it prints: b1,b2,b3,b4

As you can see from the output, the $str variable contains the elements of the matrix second column, separated by comma.

If you have an array of arrays (@AoA), you can use foreach to iterate through its array references and for each inner array you can join its elements into a string using an EXPR separator.

Next you append this string into an array using the push function.

You can use either an explicit foreach loop with a block or the short form of foreach as a modifier.

See below the syntax forms in both cases:


foreach my $item (@AoA) {
 push @array, join EXPR, @{$item};
}

# or

push @array, join EXPR, @$_ foreach (@AoA);

$item is a reference to an array and because the second argument of the join function is a list or an array, you need to dereference it by using the @{$item} notations.

In the second syntax form, foreach loops through the elements of the @AoA, assigning each element in turn to $_.

To dereference the reference stored in $_, the @$_ notation was used.

To be more explicit, let’s look at an example:


#!/usr/local/bin/perl

use strict;
use warnings;

my @AoA = (
 ['a1', 'b1', 1],
 ['a2', 'b2', 2],
 ['a3', 'b3', 3],
 ['a4', 'b4', 4],
);

my @array = ();
push @array, join ',', @$_ foreach (@AoA);

print "$_\n" foreach @array;

I used the foreach loop to print the elements of the resulting array, each element on a new line.

The output is as follows:

 a1,b1,1
a2,b2,2
a3,b3,3
a4,b4,4

If you want to sort an array of arrays by the elements of the inner arrays you can use the sort function and do something as in the following code:

#!/usr/local/bin/perl
 
use strict;
use warnings;
 
# define an array of arrays
 
my @AoA = (
 [25, 49, 33, 200],
 [145, 32],
 [11, 121, 78]
);
 
# sort and print the @AoA array
 
foreach my $item1 (@AoA){
 foreach my $item2 (sort {$b <=> $a} @{$item1}){
   print "$item2 "; 
 }
 print "\n";
}
First we populate the array of arrays with a few entries.

In our example, [25, 49, 33, 200] returns a reference to the list (25, 49, 33, 200), so our @AoA array has three scalar elements that are respectively references to the following lists: (25, 49, 33, 200), (145, 32), (11, 121, 78).

Using the sort function, the above code sorts the elements of each list in a descending numerical order. To do this we used two nested foreach.

In the outer foreach, the $item1 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 sort function is invoked for the elements of the inner arrays (the three lists presented above).

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

200 49 33 25
145 32
121 78 11

I intend to give you an example how to use the sort function to sort a matrix by its columns, giving priority to the first column, next to the second and so on. Please note that the following algorithm doesn't change the order of the items in a row, but the order of the rows.

Let’s try to sort the following matrix, which has as elements either numbers or strings (well, we can’t mix the numbers and strings in the same column):

5, 'aaa', 33, 'bbb', 12
11, 'asd', 121, 'bnm', 16
5, 'aaa', 22, 'ewq', 13
5, 'abde', 123, 'aqq', 15
5, 'aaa', 33, 'ccc', 11
5, 'abde', 78, 'azxx', 14

First we’ll store this matrix in a Perl array of arrays (@AoA) and next we’ll sort this array by the indexes of the inner arrays. The elements of the @AoA array are references to the rows of the matrix, each inner array having as elements the items contained in a row. See the code first:

#!/usr/local/bin/perl

use strict;
use warnings;

# define an array of anonymous arrays
my @AoA = (
  [5, 'aaa', 33, 'bbb', 12],
  [11, 'asd', 121, 'bnm', 16],
  [5, 'aaa', 22, 'ewq', 13],
  [5, 'abde', 123, 'aqq', 15],
  [5, 'aaa', 33, 'ccc', 11],
  [5, 'abde', 78, 'azxx', 14]
);

# using Perl sort function to sort the @AoA array
@AoA = sort {
  $b->[0] <=> $a->[0] ||
  $a->[1] cmp $b->[1] ||
  $a->[2] <=> $b->[2] ||
  $b->[3] cmp $a->[3];
} @AoA; 

# print the @AoA array
foreach my $item1 (@AoA){
 foreach my $item2 (@{$item1}){
   print "$item2\t"; 
 }
 print "\n";
}

As you can see, we use the sort function to sort the array:

  • numerically descending by the first index of the inner arrays, next
  • ASCIIbetically ascending by the second index, next
  • numerically ascending by the third index, next
  • ASCIIbetically descending by the fourth index

You don’t need to sort the matrix by all its columns, for instance we don’t use the last column to sort the matrix. We used the || operator to indicate from left to right the priority of the columns in the sort processing.

Finally, the @AoA is printed using two nested foreach.

Here is the output:

11 asd 121 bnm 16
5 aaa 22 ewq 13
5 aaa 33 ccc 11
5 aaa 33 bbb 12
5 abde 78 azxx 14
5 abde 123 aqq 15

Particularly, a matrix can be written as a Perl array of arrays so our task is to remove the first element of each inner array.

This code can be used to remove the first element of each inner array of an @AoA array of arrays. To do this, you can use the shift function which removes the first element of an array.

The following code snippet is a short example:

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

The output is as follows:

b1 1
b2 2

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

Inside the foreach loop, each element of the Perl @AoA array is assigned in turn to $_ which is a reference to an inner array.

Because the shift function has an array as argument and not a reference to an array, you need to dereference the $_ reference by putting @ sign in front of the reference. So @$_ notation means the inner array referenced by $_.

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 $_.

A matrix can be written as a Perl array of arrays (@AoA) so our task is to remove the last element of each inner array. You can accomplish this by using pop against each inner array of the @AoA array. As you know, the pop function removes the last element of an array.

The following code snippet is a short example:

#!/usr/local/bin/perl
 
use strict;
use warnings;
 
my @AoA = (
 [ 'a1', 'b1', 1 ],
 [ 'a2', 'b2', 2 ],
);
 
foreach my $item (@AoA) {
 pop @{$item};
}
 
print map "@$_\n", @AoA;

The output is as follows:

a1 b1
a2 b2

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

Inside the foreach loop, each element of the @AoA array (which is a reference to an inner array) is assigned in turn to $item.

Because the pop function has an array as argument and not a reference to an array, you need to dereference the $item reference by putting @ sign in front of the reference.

To avoid any ambiguity or operators precedence rule, the reference is enclosed by braces.

So @{$item} notation means the inner array referenced by $item.

You can use a shorter form for the foreach loop too:

pop @$_ foreach (@AoA);

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.

Thus 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 $_.

Particularly, a matrix can be represented in memory as a Perl array of arrays (@AoA).

To remove the first row of the matrix, you need to remove the first element of the array of arrays, which is a reference.

To remove the first element of an array you can use the shift function.

But in our case the first element of the array is a reference to an inner array and after shift removes it, the inner array is still in memory - only the reference was removed.

I think it is a good practice to remove the inner array too, especially as it’s not a big chunk of code to manage. In the example below I show you how to do this.

See the next code snippet:

#!/usr/local/bin/perl
 
use strict;
use warnings;
 
my @AoA = (
 [ 'a1', 'b1', 1 ],
 [ 'a2', 'b2', 2 ],
 [ 'a3', 'b3', 3 ],
);
 
# remove the first element of @AoA and save it in $ref
my $ref = shift @AoA;
 
# print the @AoA
print map "@$_\n", @AoA;
 
# print the inner array referenced by $ref
print "\$ref = @$ref\n";

This code outputs:

a2 b2 2
a3 b3 3
$ref = a1 b1 1

To remove the inner array referenced by $ref, you can assign an empty list to it:

@$ref = ();

Or to completely remove the first element of the Perl @AoA array and the inner array referenced by this element, you can use one line statement, as you can see below:

@{shift @AoA} = ();