raku.gg / beginner

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>;; # localhost say %config<;port>;; # 8080
You can also create hashes from a list of alternating keys and values:
my %colors = "red", "#FF0000", "green", "#00FF00", "blue", "#0000FF"; say %colors<;red>;; # #FF0000
Or from a list of Pairs:
my %settings = (timeout => 30, retries => 3, verbose => False); say %settings<;timeout>;; # 30

Pair Objects

A Pair is a first-class object in Raku. The fat arrow => creates a Pair:
my $pair = name => "Alice"; say $pair.key; # name say $pair.value; # Alice say $pair.WHAT; # (Pair)
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()"; } # a = 1 # b = 2 # c = 3

Colon Pair Syntax

Raku offers a compact syntax for Pairs using colons:
# Boolean pairs say :verbose; # verbose => True say :!verbose; # verbose => False # String value pairs say :name<;Alice>;; # name => Alice say :bg<;red>;; # bg => red # Variable-based pairs my $port = 8080; say :$port; # port => 8080 (key from variable name) my @items = 1, 2, 3; say :@items; # items => [1 2 3] my %data = a => 1; say :%data; # data => {a => 1}
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"; # Angle brackets for literal string keys say %user<;name>;; # Alice say %user<;age>;; # 30 # Curly braces for computed keys my $key = "role"; say %user{$key}; # admin # Quote-words for multiple keys say %user<;name age>;; # (Alice 30)

Checking if a Key Exists

Use the :exists adverb:
my %data = name => "Bob", score => 0; say %data<;name>;:exists; # True say %data<;email>;:exists; # False # This is important for distinguishing "key missing" from "value is falsy" say %data<;score>;; # 0 (falsy, but the key exists) say %data<;score>;:exists; # True
You can also check for non-existence:
say %data<;email>;:!exists; # True (email does NOT exist)

Removing Keys

Use the :delete adverb:
my %data = a => 1, b => 2, c => 3; %data<;b>;:delete; say %data; # {a => 1, c => 3} # Delete returns the removed value my $removed = %data<;c>;:delete; say $removed; # 3 say %data; # {a => 1}
You can delete multiple keys at once:
my %conf = host => "localhost", port => 8080, debug => True, verbose => True; %conf<;debug verbose>;:delete; say %conf; # {host => localhost, port => 8080}

Iterating Over Hashes

.keys, .values, .kv

my %fruit = apple => 1.50, banana => 0.75, cherry => 3.00; say %fruit.keys; # (apple banana cherry) - order may vary say %fruit.values; # (1.5 0.75 3) - corresponding order
The .kv method returns an interleaved list of keys and values:
for %fruit.kv -> $name, $price { say "$name costs \$$price"; } # apple costs $1.5 # banana costs $0.75 # cherry costs $3

.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; # Sort by key for %scores.sort(*.key) -> (:$key, :$value) { say "$key: $value"; } # alice: 95 # bob: 87 # charlie: 92 # Sort by value (descending) for %scores.sort({ -.value }) -> (:$key, :$value) { say "$key: $value"; } # alice: 95 # charlie: 92 # bob: 87

Hash Slices

You can access multiple values at once:
my %data = a => 1, b => 2, c => 3, d => 4, e => 5; # Get multiple values say %data<;a c e>;; # (1 3 5) # Assign to multiple keys %data<;x y z>; = 24, 25, 26; say %data<;x>;; # 24

Merging Hashes

There are several ways to combine hashes:
my %defaults = host => "localhost", port => 8080, debug => False; my %user = port => 3000, debug => True; # Later values override earlier ones my %merged = %defaults, %user; say %merged; # {debug => True, host => localhost, port => 3000}
Using the slip operator:
my %a = x => 1, y => 2; my %b = y => 20, z => 30; my %combined = |%a, |%b; say %combined; # {x => 1, y => 20, z => 30}

Hash Methods Reference

Here is a quick reference of commonly used hash methods:
my %h = a => 1, b => 2, c => 3; say %h.elems; # 3 (number of key-value pairs) say %h.keys; # (a b c) say %h.values; # (1 2 3) say %h.kv; # (a 1 b 2 c 3) interleaved say %h.pairs; # (a => 1 b => 2 c => 3) say %h.antipairs; # (1 => a 2 => b 3 => c) reversed pairs say %h.invert; # (1 => a 2 => b 3 => c) same idea say %h.Bool; # True (non-empty hash) say %h.Int; # 3 (same as .elems in numeric context)

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>;; # web-01 say %server<;specs>;<cpu>;; # 4 cores say %server<;specs>;<disks>;[0]; # sda say %server<;tags>;[1]; # web

Autovivification

Raku autovivifies hash keys when you assign to nested paths:
my %data; %data<;users>;<alice>;<role>; = "admin"; say %data; # {users => {alice => {role => admin}}}
However, reading a non-existent nested key returns Any, not an error:
my %data; say %data<;foo>;<bar>;; # (Any) no error, just undefined

Typed Hashes

You can constrain what types of values a hash holds:
my Int %scores; %scores<;alice>; = 95; %scores<;bob>; = 87; # %scores<charlie> = "high"; # ERROR: Type check failed
You can also constrain key types:
my %lookup{Int} = 1 => "one", 2 => "two", 3 => "three"; say %lookup{2}; # two

Practical Example: Word Frequency Counter

#!/usr/bin/env raku 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 # Count word frequencies my %freq; for $text.words -> $word { %freq{$word.lc}++; } # Display results sorted by frequency (descending) 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

#!/usr/bin/env raku # Parse a simple key=value config format sub parse-config(Str $content --> Hash) { my %config; for $content.lines -> $line { # Skip comments and blank lines next if $line ~~ /^\s*'#'/ || $line.trim eq ""; if $line ~~ /^(\w+) \s* '=' \s* (.+)$/ { %config{~$0} = ~$1; } } return %config; } my $config-text = q:to/END/; # Database settings host = localhost port = 5432 name = myapp_production # App settings 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.