How to deal with hashes of arrays - Part 2

Let’s say you have a Perl hash of arrays (%HoA), you can use the pop function to delete the last element of each inner array of the %HoA hash. The following code snippet is a short example:

#!/usr/local/bin/perl
 
use strict;
use warnings;
 
my %HoA = (
 colors => ['blue', 'white', 'red', 'green', 'yellow'],
 shapes => ['square', 'triangle', 'circle', 'rectangle'],
);
 
pop @$_ foreach (values %HoA);
 
# print the %HoA
print "$_ => @{$HoA{$_}}\n" foreach (keys %HoA);
The [] is the array constructor and returns a reference to the list enclosed in square brackets.

The first foreach steps through the values of the %HoA hash. Each value is a reference to an inner array so you need to use the @ symbol to dereference the array reference.

For each inner array the Perl pop function will remove the last element of the array.

Finally, we print the %HoA hash using a foreach loop too.

The foreach loop will step through the keys of the %HoA hash, assigning in turn each key to $_. Here $HoA{$_} means the value associated with the hash key stored in $_.

This value is a reference to an inner array, so you need to wrap it in braces and prepend with a @ symbol in order to dereference it for the print function.

The output of this code is as following:

    shapes => square triangle circle
    colors => blue white red green

 

Let’s say you have a Perl hash of arrays (%HoA), you can use the pop function to delete the last element of each inner array of the %HoA hash. The following code snippet is a short example:

#!/usr/local/bin/perl
 
use strict;
use warnings;
 
my %HoA = (
 colors => ['blue', 'white', 'red', 'green', 'yellow'],
 shapes => ['square', 'triangle', 'circle', 'rectangle'],
);
 
pop @$_ foreach (values %HoA);
 
# print the %HoA
print "$_ => @{$HoA{$_}}\n" foreach (keys %HoA);
The [] is the array constructor and returns a reference to the list enclosed in square brackets.

The first foreach steps through the values of the %HoA hash. Each value is a reference to an inner array so you need to use the @ symbol to dereference the array reference.

For each inner array the pop function will remove the last element of the array.

Finally, we print the %HoA hash using a foreach loop too.

The foreach loop will step through the keys of the %HoA hash, assigning in turn each key to $_. Here $HoA{$_} means the value associated with the hash key stored in $_.

This value is a reference to an inner array, so you need to wrap it in braces and prepend with a @ symbol in order to dereference it for the print function.

The output of this code is as following:

    shapes => square triangle circle
    colors => blue white red green

 

I’ll show you here an example about how to use delete to clear a Perl hash of arrays.

The keys function returns a list with the hash keys and the foreach loop is used to traverse this list.

# clear the HoA
foreach (keys %HoA) {
  @{$HoA{$_}} = ();
  delete $HoA{$_}; 
}
Inside the Perl foreach loop, each key of the hash is assigned in turn to $_ and:
 
  • the inner array corresponding to the current key is cleared
  • the correspondent hash entry is deleted

For more details, see "How to delete a particular hash entry".

 

Let’s say you have a %HoA Perl hash of arrays and you need to insert new elements in front of a particular inner array.

To insert new elements in front of an inner array corresponding to a specific hash key, you can use unshift with the following syntax:

unshift @{$HoA{key}}, list;
where:
 
  • list is a list of elements to insert in front of the inner array
  • key is a key of the %HoA hash of arrays
  • $HoA{key} means the reference to the inner array
  • because unshift function expects an array as its first argument, you need to dereference the inner array reference by applying @{}

See an example in the following code snippet:

#!/usr/local/bin/perl
 
use strict;
use warnings;
 
my %HoA = (
  a => [qw(abacus ant antique asset)],
  b => [qw(basket bed bird)], 
  c => [qw(car cinema)],
);
 
# insert new elements to @HoA
unshift @{$HoA{b}}, 'ball', 'belly';
 
# print the %HoA
print "$_ => @{$HoA{$_}}\n" foreach (keys %HoA);
This code produces the following output:
 
    c => car cinema
    a => abacus ant antique asset
    b => ball belly basket bed bird

Please note the using of:

  • the [] array constructor that returns a reference to an anonymous array
  • the qw operator needed to simplify quoting string values

Please note that the condition of the existence of the hash key is not necessary. You can add a new member to your hash table by using the same syntax, either your hash key exists or not:

unshift @{$HoA{d}}, 'doll', 'deer';
Finally, the %HoA hash is printed using Perl foreach to loop through its keys.

To print the %HoA with the inner array elements sorted in an ascending order, you could use the following code:

foreach my $key (keys %HoA) {
  print "$key => ", map {"$_ "} sort {$a cmp $b} @{$HoA{$key}};
  print "\n";
}
This time the output will be:
 
    c => car cinema
    a => abacus ant antique asset
    b => ball basket bed belly bird

Let’s say you have a %HoA Perl hash of arrays and you need to append new elements to a particular inner array. To append new elements to an inner array corresponding to a specific hash key, you can use the push function with the following syntax:

push @{$HoA{key}}, list;
where:
 
  • list is a list of elements to append at the end of the inner array
  • key is a key of the %HoA hash of arrays
  • $HoA{key} means the reference to the inner array corresponding to the hash key
  • because push function expects an array as its first argument, you need to dereference the inner array reference by applying @{}

See an example in the following code snippet:

#!/usr/local/bin/perl
 
use strict;
use warnings;
 
my %HoA = (
  a => [qw(abacus ant antique asset)],
  b => [qw(basket bed bird)], 
  c => [qw(car cinema)],
);
 
# append new elements to @HoA
push @{$HoA{b}}, 'ball', 'belly';
 
# print the %HoA
print "$_ => @{$HoA{$_}}\n" foreach (keys %HoA);
This code produces the following output:
 
    c => car cinema
    a => abacus ant antique asset
    b => basket bed bird ball belly

Please note the using of:

  • the [] array constructor that returns a reference to an anonymous array
  • the qw operator needed to simplify quoting string values

Please note that the condition of the existence of the hash key is not necessary. You can add a new member to your hash table by using the same syntax, either your hash key exists or not:

push @{$HoA{d}}, 'doll', 'deer';
Finally, the %HoA is printed using foreach to loop through its keys.

To print the Perl %HoA with the inner array elements sorted in an ascending order, you could use the following code:

foreach my $key (keys %HoA) {
  print "$key => ", map {"$_ "} sort {$a cmp $b} @{$HoA{$key}};
  print "\n";
}
This time the output will be:
 
c => car cinema
a => abacus ant antique asset
b => ball basket bed belly bird

The following example uses the Perl splice function to concatenate two matrices. We have two matrices:

         a11 a12 a13                   a14 a15
   HoA = a21 a22 a23         tempHoA = a24 a25
         a31 a32 a33                   a34 a35

and we want to append the columns of the tempHoA matrix to the HoA matrix, getting as result:

          a11 a12 a13 a14 a15
    HoA = a21 a22 a23 a24 a25
          a31 a32 a33 a34 a35

This time we’ll represent the matrices as hashes of arrays (%HoA).

See the following snippet:

#!/usr/local/bin/perl
 
use strict;
use warnings;
 
# initialize the hash of arrays
my %HoA = (
  line1 => [qw(a11 a12 a13)],
  line2 => [qw(a21 a22 a23)],
  line3 => [qw(a31 a32 a33)],
);
 
my %tempHoA = (
  line1 => [qw(a14 a15)],
  line2 => [qw(a24 a25)],
  line3 => [qw(a34 a35)],
);
 
foreach my $line (keys %HoA) {
  splice @{$HoA{$line}}, 3, 0, @{$tempHoA{$line}};
} 
 
# now you can clear %tempHoA
foreach (keys %tempHoA) {
  @{$tempHoA{$_}} = ();
  delete $tempHoA{$_}; 
}
 
# print the %HoA
print "@{$HoA{$_}}\n" foreach (sort keys %HoA);
The output is as follows:
 
    a11 a12 a13 a14 a15
    a21 a22 a23 a24 a25
    a31 a32 a33 a34 a35

The [] is the array constructor and returns a reference to the list enclosed in the square brackets. To quote the element of an array, the qw operator is used.

The first foreach loop is going through the keys of the %HoA hash of arrays, assigning in turn each key to the $line scalar variable. The value associated with this key is a reference to an anonymous array and to dereference it we use the @{$HoA{$line}} notation.

The second foreach loop is used to clear the %tempHoA hash.

To preserve the order of the hash, I used the Perl sort function to get the keys in an ascending lexicographical order. (another way is to use the Tie::IxHash module).

Please note that it were printed only the inner arrays of the %HoA (each hash value corresponds to an anonymous array having as elements the numbers of a matrix raw).

 

A Perl hash of arrays (%HoA) is a hash that has as values references to arrays. To copy a hash of arrays into a new one there are two ways:

A shallow copy – it assumes to copy the content of the hash of arrays into a new hash. You can do this by a simple assignment, as you can see below:

my %newHoA = %HoA;
Please note that by using this method you just copy the hash elements but the content of the anonymous arrays referenced by the hash values is not copied. That means that the values of the two hashes share the same inner arrays. If you change the content of an inner array, both %HoA and %newHoA are changed as their values point to the same anonymous array.

A deep copy – it assumes to copy the keys, the values and the anonymous arrays pointed by the references stored in the hash values. In this case the values associated with the same key in the two hashes will point to different memory locations.

The following example shows you how to use a recursive subroutine to copy each of the data contained in the hash of arrays:

#!/usr/local/bin/perl
 
use strict;
use warnings;
 
my %HoA = (
  colors => ['blue', 'white', 'red', 'green', 'yellow'],
  shapes => ['square', 'triangle', 'circle', 'rectangle'],
);
 
my %newHoA = clone(%HoA);
 
# for the hash having colors key, we alter the second element
# of the inner array referenced by the value associated with
# this key - from 'white' to 'grey'
 
$HoA{colors}[1] = 'grey';
 
sub clone {
  map { ! ref() ? $_ : [clone(@$_)] } @_;
}
 
# print the @HoA
print "\@HoA:\n";
print "$_: @{ $HoA{$_} }\n" foreach (keys %HoA);
# print the @newHoA
print "\n\@newHoA:\n";
print "$_: @{ $newHoA{$_} }\n" foreach (keys %newHoA);
The output is as follows:
 
    @HoA:
    shapes: square triangle circle rectangle
    colors: blue grey red green yellow

    @newHoA:
    shapes: square triangle circle rectangle
    colors: blue white red green yellow

In this example we use the clone() subroutine to copy all the elements of our hash of arrays.

Inside the body of the subroutine we use the map function that loops through @_ (the special @_ array has as elements the values passed to the subroutine).

At each iteration step the current element of the @_ array is assigned in turn to $_.

Inside the map block the ? ternary operator and the ref function are used to test if an element of @_ array is a reference.

The map function will return:

  • the value stored in $_ if this value is not a reference, otherwise
  • a reference to a new independent anonymous array created by the [] array constructor; in the same time we need to call the subroutine again to copy the elements of the array referenced by $_

After the hash was duplicated, in %HoA hash the value of the colors key is altered: the second element of the inner array referenced by the value associated with this key will be changed from 'white' to 'grey'.

As you can see from the output, the contents of the two arrays are different, the %newHoA hash haven’t been affected by this change.

For more complicated structures you can use the Storable module which provides the dclone function that allows you to do recursively copies too (See perlfaq4).

 

I’ll illustrate below how to use a Perl hash of arrays (%HoA) within a subroutine. A common way to do this is by passing the Perl hash of arrays by reference.

See a simple example below:

#!/usr/local/bin/perl
 
use strict;
use warnings;
 
my %HoA = (
  1 => ['a11', 'a12'],
  2 => ['a21', 'a22'],
);
 
# invoke the subroutine
myPrint(\%HoA);
 
sub myPrint{
 my $hashRef = shift;
 
 foreach my $key ( keys %$hashRef) {
  print "$key => @{ $$hashRef{$key} }\n";
 }
}
This script produces the following output:

    1 => a11 a12
    2 => a21 a22

First we populate the %HoA hash of arrays with a few entries.

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

myPrint subroutine is used to print the hash of arrays. It has as argument a reference to a hash of arrays.

Inside the subroutine body we use the shift function to discharge the argument, assigning it to the $hashRef scalar variable. So in $hashRef we have a reference to our hash of arrays. To dereference the array references, we prefix them with an @ sign and to dereference the hash reference we prefix it with the % sign.

To print the Perl hash of arrays we used a Perl foreach loop with the keys function.

In a similar way you can modify the subroutine and write your own code in order to perform inside its body whatever you need. 

 

In a hash of arrays (%HoA), you can use the exists function to avoid autovivification when you don’t intend to use it.

See the following example:

#!/usr/local/bin/perl
 
use strict;
use warnings;
 
# initialize the hash of arrays
my %HoA = (
  colors => ['white', 'blue'],
  shapes => ['oval', 'circle', 'rectangle'],
);
 
# autovivification
$HoA{tastes}[1] = 'sour'; 
defined $HoA{numbers}[2] || print "not found\n";
 
use Data::Dumper;
print Dumper \%HoA;
First we populate a Perl hash of arrays with a few entries. A Perl hash of arrays is a hash whose values are references to arrays. To get references to anonymous arrays, the [] array constructor was used.

Now let’s pay a bit of attention to this code.

The first assignment statement:

$HoA{tastes}[1] = 'sour'; 
adds an entry to our hash of arrays. Because $HoA{tastes} doesn’t exist it will be created with an appropriate value, so you don’t need to create yourself the inner anonymous array ($HoA{tastes} = []). Because we assigned the 'sour' value to the second element of the anonymous array (element having the index 1), Perl we’ll create the first element of the array too, assigning it with the undef value – see the output. This process is called autovivification and it is very useful when you have to deal with this kind of assignments. The expression can be arbitrary complicated and Perl will create all the structures it needs to make the assignment.

But if you look at the following statement:

defined $HoA{numbers}[2] || print "not found\n";
first it will be evaluated the defined $HoA{numbers}[2] expression and because the result is false the print function will be executed. But in the process of evaluation Perl needs to create the numbers key which will remain as a key entry in the %HoA hash of arrays. This time the process of autovivification enlarged our %HoA structure with an unnecessary entry – a key with an empty array reference.

Please note the using of || short-circuit operator that evaluates the second operand only if the first operand is evaluated false.

To see what is happening, I printed the hash using the Data::Dumper module. The output of this script is as follows:

not found
$VAR1 = {
          'tastes' => [
                        undef,
                        'sour'
                      ],
          'numbers' => [],
          'shapes' => [
                        'oval',
                        'circle',
                        'rectangle'
                      ],
          'colors' => [
                        'white',
                        'blue'
                      ]
        };

As you can see the numbers key has associated as value an empty array reference.

As I mentioned at the beginning of this script, to avoid autovivification in this last case, you can use the exists function:

if(exists $HoA{numbers} && defined $HoA{numbers}[2]) {
  print "found\n"; 
};
Here we use the && short-circuit operator, first we test if $HoA{numbers} exists and only afterwards we check if the $HoA{numbers}[2] expression is defined.

Please note the using of && short-circuit operator that evaluates the second operand only if the first operand is evaluated true.