Build Perl Hashes of Arrays with References
You will finish with a small Perl program that groups city names by country, then prints a sorted report. The useful idea is a hash whose values are references to arrays. The examples match perlreftut(1) from Perl 5.38.2, provided by perl-doc 5.38.2-3.2ubuntu0.6 on this machine.
The route
Jump straight to the step you need, or tick off Done means at the end.
Allow about fifteen minutes. You need Perl and a shell. The commands create files only in the directory you choose; no elevated privileges are needed. Keep the input file until you have checked the output.
1. Check the installed Perl
Confirm the interpreter and documentation package before relying on version-sensitive examples:
$ perl --version | sed -n '1,3p'
This is perl 5, version 38, subversion 2 (v5.38.2) built for x86_64-linux-gnu-thread-multi
$ dpkg-query -W -f='${Package} ${Version}\n' perl-doc
perl-doc 5.38.2-3.2ubuntu0.6
Checkpoint: if perl-doc is absent, the interpreter may still run the examples, but this installed tutorial will not be available through the local manual-page package. Install packages through your normal system process rather than modifying a production host during this exercise.
2. Make references to arrays and hashes
A reference is a scalar that identifies another Perl value. Put a backslash before a named array or hash, or use square or curly brackets to create an anonymous one:
my @colours = ('blue', 'jade');
my %counts = (blue => 2, jade => 1);
my $array_ref = \@colours;
my $hash_ref = \%counts;
my $new_array = [ 'red', 'murasaki' ];
my $new_hash = { width => 80, height => 25 };
$array_ref and $new_array are scalar variables containing array references. The brackets create a new anonymous array or hash and return its reference. An empty [] or {} creates an empty referred-to collection.
Do not confuse a scalar reference with an array of the same name. $array_ref is the reference; @array_ref would be a separate, unrelated array.
3. Read and change the referred-to value
The explicit form puts the reference inside braces. Use @{...} or %{...} for the whole collection, and a scalar dereference for one element:
my $aref = [ 10, 20, 30 ];
my $href = { red => 17 };
push @{ $aref }, 40;
$aref->[1] = 21;
$href->{blue} = 23;
print join(',', @{ $aref }), "\n";
print $href->{red}, "\n";
Output:
10,21,30,40
17
The arrow form is the usual abbreviation for an element. $aref->[1] means the second element of the array referred to by $aref. $href->{blue} means the value under blue in the referred-to hash. The arrow is required for this clear, direct form. Without it, $aref[1] refers to an element of the unrelated array @aref.
4. Build a hash of arrays from input
Save this sample data as cities.txt:
Chicago, USA
Frankfurt, Germany
Berlin, Germany
Washington, USA
Helsinki, Finland
New York, USA
Now save the program as group-cities.pl:
use strict;
use warnings;
my %cities_by_country;
while (<>) {
chomp;
my ($city, $country) = split /, /, $_, 2;
push @{ $cities_by_country{$country} }, $city;
}
for my $country (sort keys %cities_by_country) {
my @cities = sort @{ $cities_by_country{$country} };
print "$country: ", join(', ', @cities), ".\n";
}
Run it with the input file:
$ perl group-cities.pl cities.txt
Finland: Helsinki.
Germany: Berlin, Frankfurt.
USA: Chicago, New York, Washington.
The <> operator reads files named on the command line, so there is no hard-coded input path. The hash key is the country. Its value becomes a reference to an array of cities.
5. Understand autovivification before using it in a batch
The push line does not first initialise an empty array. When a country is new, Perl creates the missing hash entry and anonymous array as part of the attempted array operation. This behaviour is called autovivification. It keeps the example short, but it can also create data when a misspelled key is used.
For input you trust and a deliberately simple structure, the compact form is useful. If a missing key should be an error, check it explicitly before updating:
die "missing country\n" unless defined $country && length $country;
push @{ $cities_by_country{$country} }, $city;
That check rejects a blank country, but it does not validate the city spelling or duplicate records. Add those rules explicitly if the file is an import rather than a small local report. Do not assume that a successful Perl exit means the data is semantically correct.
6. Avoid accidental shared data
Assigning one reference to another copies the reference, not the underlying array:
my $first = [ 'Berlin' ];
my $second = $first;
$second->[0] = 'Leeds';
print $first->[0], "\n";
Output:
Leeds
Both scalars point to the same array. To make a shallow copy of the array, construct a new anonymous array from its contents:
my $copy = [ @{ $first } ];
This copies the elements into a new outer array. If an element is itself a reference, the nested value is still shared. Copy nested structures deliberately, level by level, when independent updates are required.
7. Verify the result safely
Run Perl's syntax check before changing the script:
$ perl -c group-cities.pl
group-cities.pl syntax OK
Then rerun the program and compare its output with the expected three lines. If you change the input or script, use a new output file or redirect to a new name first. Shell redirection with > truncates an existing destination before Perl starts:
$ perl group-cities.pl cities.txt > report.txt.new
$ test -s report.txt.new && mv report.txt.new report.txt
If the command fails, report.txt remains untouched. Remove the incomplete temporary file with rm report.txt.new only after checking its exact name. There is no reason to use sudo for these files.
Done means
- You can create named and anonymous array or hash references.
- You use
->to address elements of the referred-to collection. - Your program groups and sorts the sample city data into three expected lines.
- You understand that
pushcan autovivify a missing nested array. - You know that copying a reference aliases the same data, while a bracketed copy creates a new outer array.
- You ran
perl -cand kept replacement output separate until it was verified.