Perl Part I · Module 6 · Hashes
Hash Slices
Chapter 5.3 read several array positions in one expression; this is the same idea for keys. You give a list of keys inside the braces and get a value back for each, which turns pulling four named fields out of a parsed record into one statement instead of four. It also completes the sigil story from Chapter 6.1: a hash slice produces several values, so it takes the several-values sigil — an at sign in front of a variable you declared with a percent sign, which looks wrong until you remember that the sigil describes the result rather than the container. The one thing to carry forward is inherited from the array version: a slice always returns one element per key you asked for, so asking for a key that is not there succeeds and hands you nothing.
Intermediate13 min readPerlPart IHashesParsing
Part I · Module 6 · Chapter 6.7 · Hash Slices
1. Several Keys at Once
use strict;
use warnings;
my %result = (alu => "PASS", fifo => "FAIL", decoder => "PASS", regfile => "FAIL");
my @two = @result{ qw(alu decoder) };
printf "slice : %s\n", join(" ", @two);
my ($alu, $fifo) = @result{ qw(alu fifo) };
printf "named : alu=%s fifo=%s\n", $alu, $fifo;slice : PASS PASS
named : alu=PASS fifo=FAILA hash slice puts a list of keys in the braces and returns a value for each, in the order you asked.
The second form is the one you will write most: a slice on the right and a list assignment on the left, which names several fields in one statement. That is Chapter 5.6 §6's list assignment feeding off a slice.
The sigil completes the table
Chapter 6.1 §2 gave three rows and deferred the third. Here it is:
| You write | You get | Sigil |
|---|---|---|
%result | the whole hash | % |
$result{alu} | one value | $ |
@result{ "alu", "fifo" } | several values | @ |
An @ in front of a %-declared variable. That looks wrong and is not — the sigil describes what the expression produces, which is the rule Chapter 5.1 §2 stated and Chapter 5.3 §1 already applied to arrays.
The braces are what say "hash". @result{...} with braces is a hash slice; @result[...] with brackets would be an array slice on a different variable entirely — the trap from Chapter 6.1 §2's callout, now with one more way to trip over it.
qw(...) from Chapter 4.4 is the natural way to write the key list, since the keys are almost always literal words.
2. Writing Several Keys at Once
use strict;
use warnings;
my %config;
@config{ qw(retries timeout verbose) } = (3, 60, 1);
printf "%s\n", join(" ", map { "$_=$config{$_}" } sort keys %config);retries=3 timeout=60 verbose=1The slice on the left names the keys to write; the list on the right supplies the values, positionally. Three keys set in one statement.
That is genuinely useful when the keys and values arrive as parallel lists — a header row and a data row, for instance:
use strict;
use warnings;
my @header = split /,/, "test,status,total,fails", -1;
my @values = split /,/, "alu_smoke_01,PASS,184,7", -1;
my %row;
@row{ @header } = @values;
printf "status : %s\n", $row{status};
printf "total : %d\n", $row{total};
printf "keys : %s\n", join(" ", sort keys %row);status : PASS
total : 184
keys : fails status test totalThat is the pattern worth taking away from this chapter. A CSV header line becomes the keys and each data line becomes the values, so the rest of the script refers to $row{status} instead of $field[1] — which means a column reordering in the tool's output stops breaking your script, and a reader no longer has to count commas.
It is Chapter 5.8's pipeline with one line added, and that line removes the whole class of bug where field 1 quietly becomes field 2.
3. A Missing Key Gives undef, Silently
use strict;
use warnings;
my %result = (alu => "PASS");
my @got = @result{ qw(alu zzz) };
printf "asked for 2, got %d\n", scalar(@got);
printf "values : [%s]\n", join("][", map { defined $_ ? $_ : "undef" } @got);
printf "keys in the hash afterwards : %d (%s)\n", scalar(keys %result), join(" ", keys %result);asked for 2, got 2
values : [PASS][undef]
keys in the hash afterwards : 1 (alu)Two keys requested, two values back, the second undef — and nothing on STDERR.
This is Chapter 5.3 §3's behaviour exactly: a slice returns one element per thing you asked for, padding with undef where there is nothing. So slicing a record that is missing a field succeeds, and the gap only appears later when the undef interpolates as nothing or prints as 0.
Note the last line: the hash still has one key. A read slice does not create the keys it fails to find, which matches Chapter 6.4 §2's finding for a single read.
If absence matters, test for it rather than reading it:
use strict;
use warnings;
my %result = (alu => "PASS", fifo => "FAIL");
my @wanted = qw(alu fifo decoder);
my @missing = grep { !exists $result{$_} } @wanted;
if (@missing) {
printf "missing key(s): %s\n", join(" ", @missing);
}
else {
printf "all present: %s\n", join(" ", @result{ @wanted });
}missing key(s): decodergrep over the wanted keys with exists — Chapter 5.10 and Chapter 6.4 — gives you the list of what is absent, which is a far better error message than a report with a gap in it.
4. Deleting a Slice
use strict;
use warnings;
my %h = (a => 1, b => 2, c => 3, d => 4);
my @removed = delete @h{ qw(a b) };
printf "removed : %s\n", join(" ", @removed);
printf "left : %s\n", join(" ", sort keys %h);removed : 1 2
left : c ddelete accepts a slice and removes every key in it, returning the removed values as a list.
That is the compact way to strip a known set of fields — dropping internal bookkeeping keys before writing a record out, for instance.
5. The Key/Value Slice Needs Perl 5.20
There is a second slice form that returns keys and values rather than just values:
use strict;
use warnings;
use v5.20;
my %result = (alu => "PASS", fifo => "FAIL", decoder => "PASS");
my %subset = %result{ qw(alu decoder) };
printf "subset has %d keys: %s\n", scalar(keys %subset),
join(" ", map { "$_=$subset{$_}" } sort keys %subset);subset has 2 keys: alu=PASS decoder=PASSNote the % sigil on the slice: %result{...} produces key/value pairs, so it takes the pairs sigil, and the result can be assigned straight to a hash. The @ form would have given you only the values, losing which key each came from.
6. Why This Matters in Engineering Work
The header-as-keys pattern is the real payoff. §2's @row{ @header } = @values; turns positional parsing into named parsing in one line, and that change removes an entire class of bug: a tool that inserts a column no longer shifts every field your script reads. For any CSV-shaped input with a header, this is worth doing by default.
Pulling a known subset out of a record — four fields out of twenty — is one statement with a slice and four with individual reads. The key list sits in one place where it can be checked against the format.
Setting defaults. @config{ qw(retries timeout) } = (3, 60); initialises a group of related settings together, which keeps them visually grouped in the source.
And the silence is the same hazard as everywhere in these two modules. A slice of a short record succeeds and gives you undef for the missing fields. Checking exists across the wanted keys — §3's grep — turns that into a message naming exactly which field was absent, which is the difference between a traceable failure and a gap in a report.
7. Common Mistakes
Using $ for a multi-key slice. $result{...} with several keys is not a slice; the sigil has to be @.
Using brackets instead of braces. @result[...] is an array slice on a different variable.
Expecting a missing key to fail. The slice returns undef in that position, silently, and the right number of elements.
Assigning fewer values than keys. The remaining keys are created with undef values.
Using @h{...} when you wanted the keys too. The @ form returns values only; %h{...} returns pairs and needs Perl 5.20.
Using %h{...} without declaring use v5.20. It is a syntax error on older Perls.
Skipping the count check in the header pattern. You have the header, so you know the expected field count — compare it.
8. Exercise
Exercise 1 — read a slice. From a four-key status hash, pull two values with a slice and then the same two into named variables with a list assignment. Print both results.
Exercise 2 — the sigil table. For one hash, write the three expressions from §1's table and print what each gives. Say in one sentence what decides the sigil.
Exercise 3 — write a slice. Initialise three configuration keys in one statement, then print them sorted.
Exercise 4 — header as keys. Split a header line and a data line, build a hash with @row{ @header } = @values;, and print two fields by name. Then swap two columns in both lines and confirm your print statements still work unchanged.
Exercise 5 — the short row. Use a three-field header with a two-field data row. Report the key count, which key is undefined, and confirm STDERR is empty. Then add the count guard and confirm it fires.
Exercise 6 — find what is missing. Given a hash with two of three expected keys, use grep and exists to produce the list of absent keys. Confirm the message names the right one.
Exercise 7 — both subset forms. Build a two-key subset of a larger hash with %h{...} under use v5.20, and again with the portable map form. Confirm the two hashes are identical, then say which you would commit for a script running on an unknown Perl.
9. Summary
@h{ key1, key2 }is a hash slice and returns one value per key, in the order asked.- The sigil is
@because the result is several values — the braces are what make it a hash.@h[...]with brackets is a different variable. - A slice is assignable:
@h{ @keys } = @values;sets them all positionally. @row{ @header } = @values;turns a CSV header into named fields in one line, which is the chapter's most useful pattern.- A missing key gives
undefin that position, silently, and a read slice does not create the key. - Fewer values than keys creates the remaining keys with
undef— so check the counts. delete @h{ @keys }removes a set and returns the removed values.%h{ ... }returns key/value pairs and requires Perl 5.20;map { $_ => $h{$_} }is the portable equivalent.
Chapter 6.8 covers the single most-used hash idiom in Perl — counting how often something appears, which takes one line and relies on Perl creating the entry for you.
Where this fits
Part of the Perl curriculum.
