How to deal with arrays of hashes - Part 1

The following example shows you how to generate a Perl array of hashes using a while loop and push.

#!/usr/local/bin/perl

use warnings;
use strict;

my @AoH;
while(<DATA>) {
 chomp;
 push @AoH, {split};
}

# print the @AoH array 

foreach my $item ( @AoH ){
 foreach my $key ( keys %{ $item } ){
   print "$key=$item->{ $key }\t"; 
 }
 print "\n";
}

__DATA__
name John age 25
name Peter age 27
name Anne age 35
In order to read the elements of the @AoH 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 hash constructor and it will return a reference to the anonymous hash (every two elements from the anonymous array are grouped as a hash (key, value) pair element)
  • the push function will append this reference to the @AoH array

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

The output produced by this code is as follows:

    name=John age=25
    name=Peter age=27
    name=Anne age=35

If you want an alternative to push, instead of:

 push @AoH, {split};
you can use:
@AoH = (@AoH, {split});

The following example shows you how to loop over a Perl array of hashes using for statement and how to print its elements.

Don’t mix for with foreach, even if sometimes you spell foreach as for.

The for statement isn’t very often used to loop over the array elements so the following example has a demonstrative purpose only.

Look at the following sample:

#!/usr/local/bin/perl

use warnings;
use strict;

# define an array of hashes
my @AoH = (
 { name => 'John', age => 25},
 { name => 'Peter', age => 27}, 
 { name => 'Anne', age => 35},
);

# print the @AoH array 
for (my $i = 0; $i <= $#AoH; $i++) {
 my @tmp = keys %{$AoH[$i]};
 for( my $j = 0; $j <= $#tmp; $j++ ) {
   print "$tmp[$j]=$AoH[$i]->{$tmp[$j]}\t";
 } 
 print "\n";
}
The {} is the hash constructor and returns a reference to an anonymous hash whose elements are included between braces.

Thus the @AoH is an array that has 3 elements: references to the three hashes seen in the code.

To print the elements of the Perl array of hashes, we used two nested for.

In the outer for, we iterate over the indices of the @AoH array by using the $i scalar variable. The expression $#AoH means the last index of the @AoH array.

In a temporary array variable we save the keys of the hash that corresponds to the index $i of the @AoH array.

The inner for is looping through the @tmp array (which contains the current hash keys). Within its block the current hash is printed.

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

    name=John age=25
    name=Peter age=27
    name=Anne age=35

The following example shows you how to loop over a Perl array of hashes using foreach and how to print its elements.

#!/usr/local/bin/perl

use warnings;
use strict;

# define an array of hashes
my @AoH = (
 { name => 'John', age => 25},
 { name => 'Peter', age => 27}, 
 { name => 'Anne', age => 35},
);

# print the @AoH array 

foreach my $item ( @AoH ){
 foreach my $key ( keys %{ $item } ){
   print "$key=$item->{ $key }\t"; 
 }
 print "\n";
}
The {} is the hash constructor and returns a reference to the anonymous hash whose elements are included between braces. So the @AoH is an array that has 3 elements: references to the three hashes seen in the code.

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

In the outer foreach, the $item is assigned in turn to the elements of the @AoH array, so it contains a reference to a hash.

In the inner foreach, we used the %{ $item } notation to dereference the $item reference. The keys function will return the keys of the hash referenced by $item. The $item->{$key} notation means the value associated with the $key of the hash referenced by $item.

If you prefer, you can loop over the @AoH using the indices:

foreach my $i ( 0 .. $#AoH ){
 foreach my $key ( keys %{ $AoH[$i] } ){
   print "$key=$AoH[$i]{$key}\t"; 
 }
 print "\n";
}
In both cases, you’ll get as output:
 
name=John age=25
name=Peter age=27
name=Anne age=35 

The following example shows you how to traverse a Perl array of hashes (@AoH) using the grep function.

In the following example we use grep to remove from our array those elements whose corresponding hash has the value of the key units less or equal than 30.

See the code:

#!/usr/local/bin/perl

use warnings;
use strict;

# define an array of hashes
my @AoH = (
 { id => 'item1', units => 25},
 { id => 'item2', units => 35}, 
 { id => 'item3', units => 17},
);

@AoH = grep {$_->{units} > 30} @AoH;

# print the @AoH array 
use Data::Dumper;
print Dumper(\@AoH);
We populate the @AoH array with a few entries. To traverse the array, the grep loop is used.

At each iteration step, the current element of the array (which is a reference to an anonymous hash) is stored in the special variable $_.

Inside the grep block the value associated with the key units of the current hash is evaluated – if it is true, the the hash reference is returned to the same array.

At the end of the loop @AoH array will contain only the hash references we are after.

This time the resulting @AoH array is printed using the Data::Dumper module:

$VAR1 = [
          {
            'id' => 'item2',
            'units' => 35
          }
        ];

If you want the indices of the Perl @AoH array instead of hash references themselves, you can use the following code:

my @ind = grep {$AoH[$_]{units} > 30} 0..$#AoH;
print "@ind\n";
# it prints 1

You can use the Perl map function to print an array of hashes (@AoH).

The following program provides a small example:

#!/usr/local/bin/perl

use strict;
use warnings;

my @AoH = (
 {name => 'John', age => 21},
 {name => 'Paul', age => 35},
 {name => 'Mary', age => 50},
);

# print the @AoH
print join "\n",
     map {$_->{name}." is ".$_->{age}." years old."} @AoH;
print "\n";
This program would produce the following output:
 
    John is 21 years old.
    Paul is 35 years old.
    Mary is 50 years old.
 
To print the Perl array of hashes, the join and map functions were used. At each iteration step, the current element of the array (i.e. a hash reference) is stored in the $_ variable.

Please note the usage of the concatenation operator (.) here and the arrow notation (->{}), used to access the elements of a hash table given by a reference. The join function is used to separate the list elements returned by map with the newline character.

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

See the following code snippet:

#!/usr/local/bin/perl

use warnings;
use strict;

# define an array of hashes
my @AoH = (
 {id => 'item1', units => 25},
 {id => 'item2', units => 33}, 
 {id => 'item3', units => 17},
);

my $i=0;
while($i <= $#AoH) {
 while(my ($key, $val) = each % {$AoH[$i]}) {
   print("$key=>$val\t");
 }
 print "\n";
 $i++;
}
The {} is the hash constructor and returns a reference to the anonymous hash enclosed in curly brackets.

We used two nested while loops. The outer while loop iterates through the indices of the @AoH array, using the $i scalar variable.

The inner while loop uses each to iterate through the hash corresponding to the $i index of the array.

The each function expects a hash as argument so you need to dereference the hash reference by prefixing it with the % sign.

The $i scalar variable is initialized with 0 before the outer while starts, being incremented at the end of current iteration.

The output produced by this code is as follows:

    id=>item1 units=>25
    id=>item2 units=>33
    id=>item3 units=>17

Let’s say that you have a Perl array of hashes (@AoH) and you want to access or modify the elements of a particular inner hash.

To make the things easier to follow, I’ll illustrate this on a simple array of hashes like the following one:

my @AoH = (
 {name=>'John', age=>23, residence=>'Ireland'},
 {name=>'Alice', age=>31},
);
The elements of the @AoH array of hashes are references to anonymous hashes. To return a reference to a hash, the {} hash constructor was used.

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

my $ref = $AoH[0];
To access and modify the value associated with the age key of this inner hash, you can use either of the two syntax forms:
$ref->{age} = 45;
$$ref{age} = 45;
To make things easier, you don’t need an extra variable to store the reference, but use one of the following statements:
$AoH[0]->{age} = 45;
$AoH[0]{age} = 45;
So, you can use the $AoH[$i]{key} notation to access and eventually modify the value associated with a specific key in a particular inner hash, where:
 
  • $i means the index of the array of hashes
  • key means a particular key of the inner hash referenced by the element of the array that has the index $i

The first example shows you how to get an inner hash from a Perl array of hashes when a certain condition is met.

See the following snippet:

#!/usr/bin/perl
 
use strict;
use warnings;
 
my @AoH = (
 {name=>'John', age=>23},
 {name=>'Alice', age=>31},
 {name=>'Peter', age=>45},
);
 
my @subAoH = grep { $_->{age} > 30 } @AoH;
print join "\n",
     map {$_->{name}." is ".$_->{age}. " years old."} @subAoH;
print "\n";
This code will produce the following output:
 
    Alice is 31 years old.
    Peter is 45 years old.

First I generate a Perl array of hashes (@AoH). The {} braces means a hash constructor and it returns a reference to the hash table enclosed between braces. So @AoH is an array of hashes (an array whose elements are references to hashes).

This code extracts from @AoH an inner hash where the hash key age has a value greater than 30.

The Perl grep function will iterate through @AoH array assigning in turn each element of this array to $_. But the elements of @AoH array are hash references, so in $_ it is stored a hash reference.

The grep function returns only the hash references that point to hashes that have for the age key values greater than 30. The ->{} is the arrow notation and it is used to access the elements of a hash table given by a reference. Instead of $_->{age} notation you could use the $$_{age} notation, too.

Finally, the information from the inner hash is printed using the join and map function.

If you don’t need to save the inner hash but rather print the information from the hashes referenced by the array references, you could use the following code

print join "\n",
     map {$_->{name}." is ".$_->{age}. " years old."}
     grep { $_->{age} > 30 } @AoH;
But if you want to see if a particular hash is referenced by an element of @AoH array, you can use the if statement to check this.

For example, to check if there is a person with the name John in all the hashes referenced by the Perl @AoH elements, you can use the following code:

if( grep{ $$_{name} eq 'John' } @AoH)
{
  print "I found John\n";
}
Because of the if statement, Perl grep is used here in scalar context and it returns the number of matches (in this case it returns 1 which is equivalent with true in Perl).

If you want to extract the keys from a Perl array of hashes and print all the keys from an inner hash on the same line, you can use the following short snippet:

#!/usr/local/bin/perl
 
use strict;
use warnings;
 
my @AoH = (
 {name => 'John', age => '23'},
 {color => 'blue'},
 {width => 95, height => 121, top => 23, left => 15}
);
 
print join("\t", keys(%{$AoH[$_]})), "\n" foreach 0 .. $#AoH;
Here is how it works:
 
  • the foreach statement loops through the indices of the @AoH array of hashes (if you have an array named @A, than $#A means the last index of the array), assigning in turn each index to $_
  • the keys function returns a list with the keys of the current inner hash:
    • $AoH[$_] means a reference to the current inner hash
    • the above expression (which is a hash reference) is surrounding by curly braces and prepended with the % symbol to dereference it
  • the join function will return a string with the keys separated by the "\t"character
  • the print function will print the string returned by join, followed by a newline character (\n)

You’ll get something like the following output:

name age
color
left width top height

For each referenced hash, the keys are listed in the inner order of the hash table.

To get the keys sorted - for example in an alphabetical ascending order, you need to insert a sort block before the keys function, modifying the one line code from above with the following one:

print join("\t", sort {$a cmp $b} keys(%{$AoH[$_]})), "\n"
     foreach 0 .. $#AoH;
This time the output will be as follows:
 
    age name
    color
    height left top width

To compare the inner hash keys the string cmp operator was used.

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 Perl array of hashes (@AoH) where each inner hash corresponds to a row from the above table. We want to sort the whole array by the values of some elements of the hash.

The keys of the hash are item, mfr, cap and sp and their values represent the item code, the manufacturer, the capacity and the speed of the hard disk.

After the initialization, we will sort the array of hashes after the values of the hash:

  • alphabetically ascending after the manufacturer value, next
  • numerically descending after the speed value and last
  • numerically descending after the capacity value.

The value corresponding to the item key is not used in the sorting process.

See the code:

#!/usr/local/bin/perl
 
use strict;
use warnings;
 
my @AoH = (
 {item=>'Item1', mfr=>'Toshiba', cap=>100, sp=>4200},
 {item=>'Item2', mfr=>'Maxtor' , cap=>100, sp=>5400},
 {item=>'Item3', mfr=>'Maxtor' , cap=>100, sp=>7200},
 {item=>'Item4', mfr=>'Seagate', cap=>100, sp=>7200},
 {item=>'Item5', mfr=>'Quantum', cap=>10.2, sp=>4500},
 {item=>'Item6', mfr=>'Seagate', cap=>160, sp=>5400},
 {item=>'Item7', mfr=>'Hitachi', cap=>250, sp=>7200},
 {item=>'Item8', mfr=>'Toshiba', cap=>60, sp=>4200}
);
 
print map { "$_->{item}\t$_->{mfr}\t$_->{cap}\t$_->{sp} \n"}
 sort {
    $a->{mfr} cmp $b->{mfr} ||
    $b->{sp} <=> $a->{sp} ||
    $b->{cap} <=> $a->{cap}
 } @AoH;
This code produces the following output:

Item7 Hitachi 250 7200
Item3 Maxtor 100 7200
Item2 Maxtor 100 5400
Item5 Quantum 10.2 4500
Item4 Seagate 100 7200
Item6 Seagate 160 5400
Item1 Toshiba 100 4200
Item8 Toshiba 60 4200

The sort function has as argument a list of hash references. Within the curly brackets we can use the $a, $b special variables which are hash references.

The notation $a->{mfr} means the corresponding value of the mfr key of the hash referenced by $a.

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.Here <=> means the numerical comparison operator and cmp the string comparison operator.

The Perl map function has as argument the ordered list of references of the @AoH array returned by the sort function. It will return to the print function a list of strings to be printed. The values of each hash will be printed on a new line.

If you want to keep the array sorted, you can modify appropriately your code and assign the ordered array returned by the sort function to Perl @AoH array:

@AoH = sort {
   $a->{mfr} cmp $b->{mfr} ||
   $b->{sp} <=> $a->{sp} ||
   $b->{cap} <=> $a->{cap}
} @AoH;