Hashes and Pairs
2026-03-15
Hashes (also known as dictionaries, maps, or associative arrays in other languages) are one of the most fundamental data structures in programming. Raku gives you a rich set of tools for working with key-value data, built on top of a first-class Pair type that makes hash operations both powerful and readable.
Creating Hashes
The most common way to create a hash uses the fat arrow =>:
my %config = host => "localhost",
port => 8080,
debug => True;
say %config<host>;
say %config<port>;
You can also create hashes from a list of alternating keys and values:
my %colors = "red", "#FF0000", "green", "#00FF00", "blue", "#0000FF";
say %colors<red>;
Or from a list of Pairs:
my %settings = (timeout => 30, retries => 3, verbose => False);
say %settings<timeout>;
Pair Objects
A Pair is a first-class object in Raku. The fat arrow => creates a Pair:
my $pair = name => "Alice";
say $pair.key;
say $pair.value;
say $pair.WHAT;
Pairs are not just hash syntax. They are standalone objects you can pass around, store in arrays, and use independently:
my @pairs = (a => 1, b => 2, c => 3);
for @pairs -> $p {
say "$p.key() = $p.value()";
}
Colon Pair Syntax
Raku offers a compact syntax for Pairs using colons:
say :verbose;
say :!verbose;
say :name<Alice>;
say :bg<red>;
my $port = 8080;
say :$port;
my @items = 1, 2, 3;
say :@items;
my %data = a => 1;
say :%data;
The :$variable shorthand is especially useful. It creates a Pair where the key is the variable name and the value is the variable's content.
Accessing Hash Values
There are multiple ways to access hash values:
my %user = name => "Alice", age => 30, role => "admin";
say %user<name>;
say %user<age>;
my $key = "role";
say %user{$key};
say %user<name age>;
Checking if a Key Exists
Use the :exists adverb:
my %data = name => "Bob", score => 0;
say %data<name>:exists;
say %data<email>:exists;
say %data<score>;
say %data<score>:exists;
You can also check for non-existence:
say %data<email>:!exists;
Removing Keys
Use the :delete adverb:
my %data = a => 1, b => 2, c => 3;
%data<b>:delete;
say %data;
my $removed = %data<c>:delete;
say $removed;
say %data;
You can delete multiple keys at once:
my %conf = host => "localhost", port => 8080, debug => True, verbose => True;
%conf<debug verbose>:delete;
say %conf;
Iterating Over Hashes
.keys, .values, .kv
my %fruit = apple => 1.50, banana => 0.75, cherry => 3.00;
say %fruit.keys;
say %fruit.values;
The .kv method returns an interleaved list of keys and values:
for %fruit.kv -> $name, $price {
say "$name costs \$$price";
}
.pairs
.pairs returns a list of Pair objects:
for %fruit.pairs -> $p {
say "$p.key() => $p.value()";
}
Or destructured:
for %fruit.pairs -> (:$key, :$value) {
say "$key => $value";
}
Sorted Iteration
Hashes have no guaranteed order. To iterate in a specific order:
my %scores = alice => 95, bob => 87, charlie => 92;
for %scores.sort(*.key) -> (:$key, :$value) {
say "$key: $value";
}
for %scores.sort({ -.value }) -> (:$key, :$value) {
say "$key: $value";
}
Hash Slices
You can access multiple values at once:
my %data = a => 1, b => 2, c => 3, d => 4, e => 5;
say %data<a c e>;
%data<x y z> = 24, 25, 26;
say %data<x>;
Merging Hashes
There are several ways to combine hashes:
my %defaults = host => "localhost", port => 8080, debug => False;
my %user = port => 3000, debug => True;
my %merged = %defaults, %user;
say %merged;
Using the slip operator:
my %a = x => 1, y => 2;
my %b = y => 20, z => 30;
my %combined = |%a, |%b;
say %combined;
Hash Methods Reference
Here is a quick reference of commonly used hash methods:
my %h = a => 1, b => 2, c => 3;
say %h.elems;
say %h.keys;
say %h.values;
say %h.kv;
say %h.pairs;
say %h.antipairs;
say %h.invert;
say %h.Bool;
say %h.Int;
Nested Hashes
Hashes can contain other hashes and arrays:
my %server =
name => "web-01",
specs => {
cpu => "4 cores",
ram => "16 GB",
disks => ["sda", "sdb"],
},
tags => <production web frontend>;
say %server<name>;
say %server<specs><cpu>;
say %server<specs><disks>[0];
say %server<tags>[1];
Autovivification
Raku autovivifies hash keys when you assign to nested paths:
my %data;
%data<users><alice><role> = "admin";
say %data;
However, reading a non-existent nested key returns Any, not an error:
my %data;
say %data<foo><bar>;
Typed Hashes
You can constrain what types of values a hash holds:
my Int %scores;
%scores<alice> = 95;
%scores<bob> = 87;
You can also constrain key types:
my %lookup{Int} = 1 => "one", 2 => "two", 3 => "three";
say %lookup{2};
Practical Example: Word Frequency Counter
my $text = q:to/END/;
the quick brown fox jumps over the lazy dog
the dog barked at the fox and the fox ran away
the quick fox was too quick for the lazy dog
END
my %freq;
for $text.words -> $word {
%freq{$word.lc}++;
}
say "=== Word Frequency ===";
say sprintf("%-12s %s", "WORD", "COUNT");
say "-" x 20;
for %freq.sort({ -.value }).head(10) -> (:$key, :$value) {
my $bar = "#" x $value;
say sprintf("%-12s %2d %s", $key, $value, $bar);
}
say "\nUnique words: %freq.elems()";
say "Total words: { [+] %freq.values }";
Output:
=== Word Frequency ===
WORD COUNT
--------------------
the 7 #######
fox 4 ####
quick 3 ###
dog 3 ###
the 2 ##
lazy 2 ##
...
Practical Example: Config File Parser
sub parse-config(Str $content --> Hash) {
my %config;
for $content.lines -> $line {
next if $line ~~ /^\s*'#'/ || $line.trim eq "";
if $line ~~ /^(\w+) \s* '=' \s* (.+)$/ {
%config{~$0} = ~$1;
}
}
return %config;
}
my $config-text = q:to/END/;
host = localhost
port = 5432
name = myapp_production
debug = false
workers = 4
END
my %conf = parse-config($config-text);
for %conf.sort(*.key) -> (:$key, :$value) {
say sprintf("%-10s = %s", $key, $value);
}
What is Next?
With hashes and pairs under your belt, you have all the core data structures covered. Next up: regular expressions in Raku. If you thought regexes were powerful before, wait until you see what Raku does with them.