How to deal with hashes of arrays - Part 1

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

#!/usr/local/bin/perl
 
use warnings;
use strict;
 
my %HoA;
while(<DATA>) {
 chomp;
 s/^(.*?):\s*//;
 $HoA{$1} = [split];
}
 
# print the @HoA hash 
 
foreach my $key ( keys %HoA) {
 print "$key: @{ $HoA{$key} }\n";
}
 
__DATA__
colors: red blue green white
numbers: 1 5 77 3 432
strings: one ten
In order to read the elements of the %HoA hash, 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 substitution operator s/// searches $_ and store in $1 the string that matches the components enclosed by the first group of parentheses and removes from $_ this string, the colon(:) and any whitespace that follows the colon
  • the split function will split the content stored in $_ into an anonymous array using the whitespace delimiter; [] is the array constructor and it will return a reference to this anonymous array;
  • a new ($key, $val) pair element will be added to the hash table (in the $key will be stored the string assigned to $1 and in the $val will be stored the array reference)

Finally, a foreach loop is used to print the Perl %HoA hash.

The output produced by this code is as follows:

    numbers: 1 5 77 3 432
    strings: one ten
    colors: red blue green white

The following example shows you how to use the for statement to loop over a hash of arrays and print its elements. Don’t mix for with foreach, even if sometimes you spell foreach as for.

Look at the following sample:

use strict;
use warnings;
 
my %HoA = (
 colors => ['red', 'blue', 'green', 'white'],
 numbers => [1, 5, 77, 3, 432],
 strings => ['one', 'ten']
); 
 
my @tmp = keys %HoA;
 
for(my $i=0; $i<=$#tmp; $i++) {
 print "$tmp[$i]:";
 for(my $j=0; $j<=$#{$HoA{$tmp[$i]}}; $j++) {
   print " $HoA{$tmp[$i]}[$j]";
 }
 print "\n";
}
First we populate a Perl hash of arrays with a few entries. The hash values are references to anonymous arrays. To get references to an anonymous array, the [] array constructor was used.

To traverse and print the elements of the hash of arrays, we used two nested for. To make the code more readable, before the for loops we save the keys of the hash of arrays in the @tmp array variable. Please note that the keys function doesn’t return the keys in any specific order.

In the outer for, we iterate over the indices of the @tmp array by using the $i scalar variable. The expression $#tmp means the last index of the @tmp array. Inside the for block, first we print the current key stored in the @tmp array. Here $tmp[$i] means the element of @tmp array which has the index $i.

The inner for is looping through the elements of the array referenced by the value corresponding to the %HoA key. As I mentioned before, this key is stored in $tmp[$i]. To loop through this array, the $j variable is used. The elements of the referenced array are printed separated by space. The current element of the inner array is given by: $HoA{$tmp[$i]}[$j].

After running this code, you’ll get as output:

numbers: 1 5 77 3 432
strings: one ten
colors: red blue green white

Example 1.

The following example shows you how to use foreach to loop over a Perl hash of arrays and print its elements.

#!/usr/bin/perl
 
use strict;
use warnings;
 
my %HoA = (
 colors => ['red', 'blue', 'green', 'white'],
 numbers => [1, 5, 77, 3, 432],
 strings => ['one', 'ten']
); 
 
foreach my $key ( keys %HoA) {
 print "$key: @{ $HoA{$key} }\n";
}
We populated our %HoA hash with three pair elements having respectively as keys: colors, numbers and strings. Each key has associated as value a reference to an anonymous array, the [] is the array constructor and returns a reference to the list of elements enclosed by the square brackets.

In the foreach loop, the keys function returns a list with the keys of the %HoA hash. The construct $HoA{$key} is the hash value associated with the $key, i.e. a reference to the corresponding anonymous array.

In order to get the array, we need to dereference the array reference and precede the reference with the @ symbol.

The output of this script is as follows:

    numbers: 1 5 77 3 432
    strings: one ten
    colors: red blue green white

If you prefer, you can loop over the %HoA using two nested foreach and the indices of the anonymous arrays:

foreach my $key ( keys %HoA ) {
 print "$key: ";
 foreach my $index ( 0 .. $#{ $HoA{$key} } ) {
   print "$HoA{$key}[$index] ";
 }
 print "\n";
}

Example 2.

The following code shows you how to print a Perl %HoA hash of arrays using foreach and join:

#!/usr/local/bin/perl
 
use strict;
use warnings;
 
my %HoA = (
 colors => ["red", "yellow", "blue"],
 shapes => ["circle", "rectangle"],
 flowers => ["rose", "tulip", "snowdrop"]
);
 
foreach my $key (keys %HoA) {
 print "$key: ",
 join(", ", @{ $HoA{$key} }), "\n";
}
First, the %HoA hash is populated with a few entries. To return a reference to an anonymous array the []array constructor is used.

The foreach statement loops through the keys of the %HoA hash and at each iteration step, assigns the current key to the $key iterator variable.

Inside the foreach block:

  • $HoA{$key} – means the value associated with the hash key stored in the $key variable. Don’t forget that this value is an array reference.
  • the surrounding pair of curly brackets prepended by the @ at sign dereferences the array references, so @{$HoA{$key}} means the array associated the hash key
  • the join function creates a string from this array by separating the array elements with ", "
  • finally, the print function displays the hash key, the string returned by join and the \n newline character

Running this code produces the following output:

    flowers: rose, tulip, snowdrop
    shapes: circle, rectangle
    colors: red, yellow, blue

Now let’s take this example one step further. If you want to get the elements of the inner arrays in a specific order, you need to sort that array. You do this by inserting a sort block in front of the array:

 join(", ", sort {$a cmp $b} @{ $HoA{$key} }), "\n";
The sort is in an ASCIIbetical order – because of the cmp operator (in this particular case you can omit the block and use the sort keyword only: sort @{$HoA{$key}}) .

This time you’ll be presented with the following result:


    flowers: rose, snowdrop, tulip
    shapes: circle, rectangle
    colors: blue, red, yellow

Now I’m going to throw you a new twist – to print the %HoA Perl hash of arrays by sorting its keys in a specific order.

To get this, you need to insert a sort block before the keys function – this time we’ll use a descending ASCIIbetical order ($bis on the left side of the cmp operator).

The foreach block becomes:

foreach my $key (sort {$b cmp $a} keys %HoA) {
 print "$key: ",
 join(", ", sort {$a cmp $b} @{ $HoA{$key} }), "\n";
}
The appropriate output is shown here:
 
shapes: circle, rectangle
flowers: rose, snowdrop, tulip
colors: blue, red, yellow 

The following example shows you how to use grep to traverse a Perl hash of arrays (%HoA):

#!/usr/local/bin/perl
 
use strict;
use warnings;
 
my %HoA = (
 green => ['LightSeaGreen', 'LimeGreen', 'DarkGreen',
          'DarkSeaGreen', 'LightGreen', 'YellowGreen'],
 blue => ['LightSteelBlue', 'BlueViolet',
         'DarkBlue', 'DeepSkyBlue', 'SkyBlue' ],
 red => ['OrangeRed', 'DarkRed']
);
 
my @result = grep { "@{$HoA{$_}}" =~ /light/i } keys %HoA;
print "@result\n";
# it prints: green blue
As you can see, we have a Perl hash of arrays (%HoA) having as keys basic colors: green, blue, red. Each key has as value a reference to an anonymous array, whose elements are a few colors that belong to that basic color. We try to match the word light from all inner arrays and return an array with the %HoA keys where the match was found.

The grep function will iterate through the keys of the %HoA hash. At each iteration step, the current key is assigned in turn to $_. Let’s take a look inside the block of grep.

  • $_ contains the current key of %HoA hash
  • $HoA{$_} means the value associated with the current %HoA hash key; this value is a reference to a specific inner array
  • @{} is used to dereference the array reference; you will get as result the current inner array
  • "" is used to interpolate the above array and convert it into a string where the elements of the array are delimited by space or whatever you have in $"
  • =~ is the bind operator and let you match the string on the left side to the expression on the right side
  • /light/i is the expression to match (the presence of the i modifier let you make the search case insensitively)

If you want to get all those colors that match the word light, you can change the code, something like that:

my @result = grep {/light/i } map {@{$HoA{$_}}} keys %HoA;
Here:
 
  • the keys function returns a list with the %HoA hash keys
  • the map function loops through the list returned by keys and returns a list with all the elements from all the inner arrays
  • the grep function loops through the list returned by map; at each iteration step the current element of the list is assigned in turn to $_; next, the regular expression within the grep block is evaluated against $_ and if the result is true, the current element is added to the @result array.

You’ll get as output:

    LightSeaGreen LightGreen LightSteelBlue

You can use the map and print functions to print the elements of a Perl hash of arrays (%HoA).

You have a sample below:

#!/usr/local/bin/perl
 
use strict;
use warnings;
 
my %HoA = (
 colors => ['blue', 'white', 'red', 'green', 'yellow'],
 shapes => ['square', 'triangle', 'circle', 'rectangle'],
);
 
# print the %HoA
print map "$_ => @{$HoA{$_}}\n", keys %HoA;
First, the Perl keys function returns a list with the keys of the %HoA hash. Next, the map function goes through the hash keys list and assigns in turn each key to the $_ special variable.

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 prefix with a @ symbol in order to dereference it for the print function.

Please note the using of quotes that allow you to interpolate the inner array included between quotes, so the elements of the inner array are printed separated by space or whatever you have in $". Each array is printed on a new line (because of \n).

The output of this code is as following:

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

The following example shows you how to print the elements of a Perl hash of arrays using the while loop.

See the following code snippet:

#!/usr/local/bin/perl
 
use strict;
use warnings;
 
my %HoA = (
 colors => ['green', 'white', 'red', 'blue'],
 shapes => ['rectangle', 'circle', 'triangle', 'star']
);
 
while(my ($key, $val) = each %HoA) {
 print "$key:";
 my $i=0;
 while($i < @$val) {
   print " $val->[$i]";
   $i++;
 }
 print "\n";
}
The [] is the array constructor and returns a reference to the anonymous array enclosed in square brackets.

We used two nested while loops.

The outer while loop uses each to iterate through the %HoA hash. The each function returns the next pair element of the %HoA hash: the key in $key and the value (which is a reference to an anonymous array) in $val.

The inner while loop iterates through the indices of the array referenced by $val. To iterate through the array indices, the $i scalar variable is used.

The $i scalar variable is initialized with 0 before the inner while starts, being incremented at the end of current iteration. The loop ends when the array elements are exhausted.

As I already mentioned, in the $val variable is stored a reference to an array. You need to dereference it by prefixing the reference with the @ symbol.

Because the @$val array is compared with a scalar variable, @$val is by default evaluated in a scalar context giving you the number of the array elements.

An alternative to get the array size is to use it explicit in a scalar context, applying the scalar function: scalar @$val. Another way is to use the last index of the array, and the inner while can be rewritten as:

 while($i <= $#{$val}) {
To print the current element of the array, the $val->[$i]notation is used. The arrow operator allows you dereference the reference and $i means the current index of the current array. We used the array reference stored in $val to get the array element.

You can rewrite the print statement by using only the value stored in $key:

 print " $HoA{$key}[$i]";
Please note that in this case you don’t need the $val variable, so you can replace the outer while with a foreach that uses $key as iterator.

The output produced by this code is as follows:

    shapes: rectangle circle triangle star
    colors: green white red blue

Being given a Perl hash of arrays (%HoA) I’ll show you below how 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 hash of arrays like the following one:

my %HoA = (
 group1 => ['usr1', 'usr2', 'usr3'],
 group2 => ['usr5', 'usr4'],
);
The values of the Perl %HoA hash of arrays are references to anonymous arrays. To return a reference to an array, the [] array constructor was used.

In this example we have 2 inner arrays, for example to get a reference to the inner array referenced by the value associated with the group1 key, you can write:

my $ref = $HoA{group1};
To access and modify the second element of this inner array (the element of index 1) you can use either of the two syntax forms:
 
$ref->[1] = 'usr7';
$$ref[1] = 'usr7';
To make things easier, you don’t need an extra variable to store the reference, but use one of the two syntax forms:
 
$HoA{group1}->[1] = 'usr7';
$HoA{group1}[1] = 'usr7';
So, you can use the $HoA{key}[$i] notation to access and eventually modify an element of an inner array, where:
 
  • key means a particular key of the %HoA hash
  • $i means an index of the inner array referenced by the hash value associated with key

The following example shows you how to get an array with the keys of a Perl hash of arrays (%HoA) where the inner arrays match a certain criteria.

To filter the inner arrays content, the grep function is used.

#!/usr/local/bin/perl
 
use strict;
use warnings;
 
my %HoA = (
 group1 => ['usr1', 'usr2', 'usr3'],
 group2 => ['usr5', 'usr4', 'usr2'],
 group3 => ['usr7', 'usr8', 'usr1'],
 group4 => ['usr5', 'usr2', 'usr4'],
);
 
my @result = grep { "@{$HoA{$_}}" =~ /usr1/i } keys %HoA;
print "@result\n";
If you run this script, you’ll end up with the following output:
 
    group1 group3

As you can see, we have a Perl hash of arrays (%HoA) having as keys group1, group2, group3, group4. Each key has as value a reference to an anonymous array, whose elements are the users that belong to that group. The above example will print the groups where usr1 belongs.

The grep function will iterate through the keys of the %HoA hash. At each iteration step, the current key is assigned in turn to $_. Let’s take a look inside the block of grep.

  • $_ contains the current key of %HoA hash
  • $HoA{$_} means the hash value associated with the current hash key; this value is a reference to a specific array
  • @{} is used to dereference the array reference; you will get as result the current inner array
  • "" is used to interpolate the above array and convert it into a string where the elements of the array are delimited by space or whatever you have in $"
  • =~ is the bind operator and let you match the string on the left side to the expression on the right side
  • /usr1/i is the expression to match (the presence of the i modifier let you make the search case insensitively)

So the grep function helped us to filter out the groups where usr1 doesn’t belong.

Let’s say you have in stock a few hard drives with the following characteristics:

Item

Mfr

Cap (GB)

Speed (RPM)

Item1

Toshiba

100

4200

Item2

Maxtor

100

5400

Item3

Maxtor

100

7200

Item4

Seagate

100

7200

Item5

Quantum

10.2

4500

Item6

Seagate

160

5400

Item7

Hitachi

250

7200

Item8

Toshiba

60

4200

 

We’ll turn this table into a hash of arrays where the key is the code of the item. For each key it will correspond as value a reference to an array composed of three elements: manufacturer, capacity and speed.

After the initialization, we will sort the keys of the hash of arrays after the indexes of the array:

  • alphabetically ascending after the first index (manufacturer),
  • next numerically descending after the third index(speed) and
  • last numerically descending after the second index (capacity).

Please note that we can’t sort the hash itself, but rather return a list with the keys ordered after specific criteria. A hash order is generally random and you can’t rely on a specific order in a hash.

See the code:

#!/usr/local/bin/perl
 
use strict;
use warnings;
 
my %HoA =
(
 'Item1' => [qw(Toshiba 100 4200)],
 'Item2' => [qw(Maxtor 100 5400)],
 'Item3' => [qw(Maxtor 100 7200)],
 'Item4' => [qw(Seagate 100 7200)],
 'Item5' => [qw(Quantum 10.2 4500)],
 'Item6' => [qw(Seagate 160 5400)],
 'Item7' => [qw(Hitachi 250 7200)],
 'Item8' => [qw(Toshiba 60 4200)]
);
 
print map { "$HoA{$_}[0]\t$HoA{$_}[1]\t$HoA{$_}[2] \n"}
 sort {
   $HoA{$a}[0] cmp $HoA{$b}[0] ||
   $HoA{$b}[2] <=> $HoA{$a}[2] ||
   $HoA {$b}[1] <=> $HoA{$a}[1]
 } keys %HoA;
This code produces the following output:
 
    Hitachi 250 7200
    Maxtor 100 7200
    Maxtor 100 5400
    Quantum 10.2 4500
    Seagate 100 7200
    Seagate 160 5400
    Toshiba 100 4200
    Toshiba 60 4200

The sort function has as argument the list of keys of the %HoA hash.

$HoA{$a} is the array reference associated with the key $a, $HoA{$a}[0] is the first element of this array.

Here <=> means the numerical comparison operator and cmp the string comparison operator.

If $a is positioned at the left side of the comparison operator this gives an ascending order and at the right side of the comparison operator it gives a descending order.

I used the || operator to indicate from left to right the priority of the columns in the sort processing.

The map function has as argument the ordered list of the %HoA hash keys returned by the sort function and will return to the print function a list of strings to be printed.

Practically, for each key will be printed the elements of the array associated with that key, each array on a new line.

Another way to print the elements of the inner arrays is to use:

$" = "\t";
print map { "@{$HoA{$_}} \n"}
instead of:
 
print map { "$HoA{$_}[0]\t$HoA{$_}[1]\t$HoA{$_}[2] \n"}
 

If you have a Perl hash of arrays (%HoA), you can use the shift function to remove the first 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'],
);
 
shift @$_ 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 the square brackets.

The first foreach steps through the values of the %HoA hash, assigning in turn each value to $_. 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 shift function will remove the first element of the array.

The second Perl foreach steps 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 it with a @ symbol in order to dereference it for the print function.

The output of this code is as following:

    shapes => triangle circle rectangle
    colors => white red green yellow

If you want to save in an array the removed elements, you can use an additional push to append these elements to the array:

my @array;
push @array, shift @$_ foreach (values %HoA);
 
print "@array\n";
# it prints: square blue