Lists and Arrays
2026-03-14
Lists and arrays are the workhorses of most programs. Raku treats them with special care, giving you both a rich set of built-in methods and functional programming tools like map, grep, and reduce. In this tutorial, we will cover everything from basic array creation to powerful list transformations.
Lists vs Arrays
Raku distinguishes between Lists (immutable sequences) and Arrays (mutable containers). In practice, the difference is subtle for beginners, but worth knowing:
my @array = 1, 2, 3;
my $list = (1, 2, 3);
@array.push(4);
When you use the @ sigil, you get a mutable Array. Most of the time, this is what you want.
Creating Arrays
my @numbers = 1, 2, 3, 4, 5;
my @digits = 0..9;
my @colors = <red green blue yellow>;
my @empty;
say @empty.elems;
my @zeros = 0 xx 10;
say @zeros;
The xx operator is the list repetition operator, creating a list with the value repeated N times.
Accessing Elements
my @fruits = <apple banana cherry date elderberry>;
say @fruits[0];
say @fruits[1];
say @fruits[*-1];
say @fruits[*-2];
The -1 syntax uses the Whatever star to count from the end. This is clean and avoids off-by-one errors.
Array Slices
You can grab multiple elements at once:
my @letters = 'a'..'j';
say @letters[0, 2, 4];
say @letters[0..3];
say @letters[0, 2 ... 8];
Modifying Arrays: push, pop, shift, unshift
These four methods add and remove elements from either end:
my @stack;
@stack.push(1);
@stack.push(2, 3);
say @stack;
my $top = @stack.pop;
say $top;
say @stack;
@stack.unshift(0);
say @stack;
my $first = @stack.shift;
say $first;
say @stack;
Think of it this way:
push/pop work on the right end (like a stack)
unshift/shift work on the left end (like a queue)
append and prepend
append is like push but flattens arrays:
my @a = 1, 2, 3;
@a.push([4, 5]);
say @a;
my @b = 1, 2, 3;
@b.append(4, 5);
say @b;
my @c = 4, 5, 6;
@c.prepend(1, 2, 3);
say @c;
.elems, .end, and Boolean Context
my @items = <foo bar baz>;
say @items.elems;
say @items.end;
say ?@items;
say !@items;
my @empty;
say ?@empty;
Sorting
The .sort method returns a new sorted list:
my @nums = 5, 3, 8, 1, 9, 2;
say @nums.sort;
my @words = <banana apple cherry date>;
say @words.sort;
Custom sort with a comparison block:
my @nums = 5, 3, 8, 1, 9, 2;
say @nums.sort({ $^b <=> $^a });
my @words = <cat elephant bee dog>;
say @words.sort({ $^a.chars <=> $^b.chars });
my @files = <README.md app.js style.css index.html>;
say @files.sort(*.chars);
The .chars syntax is a WhateverCode block, equivalent to { $_.chars }.
Reversing
my @nums = 1, 2, 3, 4, 5;
say @nums.reverse;
say @nums;
grep: Filtering
.grep returns elements that match a condition:
my @nums = 1, 2, 3, 4, 5, 6, 7, 8, 9, 10;
say @nums.grep(* > 5);
say @nums.grep(* %% 2);
say @nums.grep(3..7);
my @words = <apple banana avocado cherry apricot>;
say @words.grep(/^a/);
say @words.grep(*.chars > 5);
.grep uses smart matching, so you can pass types, regexes, ranges, junctions, blocks, or plain values.
map: Transforming
.map applies a function to every element and returns the results:
my @nums = 1, 2, 3, 4, 5;
say @nums.map(* * 2);
say @nums.map(* ** 2);
say @nums.map({ "$_ squared is { $_ ** 2 }" });
my @words = <hello world>;
say @words.map(*.uc);
say @words.map(*.chars);
Chaining map and grep
This is where functional programming shines. Chain methods together to build data pipelines:
my @data = 1..20;
my @result = @data
.grep(* %% 2)
.map(* ** 2)
.head(5);
say @result;
flat: Flattening Nested Lists
my @nested = (1, 2), (3, 4), (5, 6);
say @nested.flat;
my @deep = (1, (2, (3, 4)));
say @deep.flat;
unique and squish
.unique removes duplicate values:
my @nums = 1, 2, 2, 3, 3, 3, 4, 4, 4, 4;
say @nums.unique;
my @words = <cat dog cat bird dog>;
say @words.unique;
.squish removes consecutive duplicates (useful for sorted data or run-length scenarios):
my @data = 1, 1, 2, 2, 2, 1, 1, 3, 3;
say @data.squish;
say @data.unique;
reduce: Combining All Elements
.reduce combines all elements using an operation:
my @nums = 1, 2, 3, 4, 5;
say @nums.reduce(&infix:<+>);
say @nums.reduce(&infix:<*>);
say [+] @nums;
say [*] @nums;
say [~] <a b c>;
say [max] @nums;
say [min] @nums;
The [operator] reduction meta-operator is one of Raku's most elegant features.
head and tail
Grab elements from the beginning or end:
my @nums = 1..10;
say @nums.head(3);
say @nums.tail(3);
say @nums.head;
say @nums.tail;
Ranges as Arrays
Ranges are lazy. They become arrays when you need them to:
my @evens = (0, 2 ... 20);
say @evens;
for ^5 { say $_ }
my @indices = ^10;
say @indices;
The ^N syntax is shorthand for 0..^N and is used constantly in Raku for index ranges.
zip: Combining Arrays
The zip function pairs up elements from multiple arrays:
my @names = <Alice Bob Charlie>;
my @scores = 95, 87, 92;
for @names Z @scores -> ($name, $score) {
say "$name: $score";
}
You can also zip with an operator:
my @a = 1, 2, 3;
my @b = 10, 20, 30;
say @a Z+ @b;
say @a Z~ @b;
Practical Example: Student Grades
my @students = (
{ name => "Alice", grades => [92, 88, 95, 91] },
{ name => "Bob", grades => [78, 82, 76, 80] },
{ name => "Charlie", grades => [95, 97, 93, 98] },
{ name => "Diana", grades => [65, 70, 68, 72] },
);
my @results = @students
.map({
my $avg = ([+] $_<grades>) / $_<grades>.elems;
{ name => $_<name>, average => $avg }
})
.sort({ $^b<average> <=> $^a<average> });
say "=== Grade Report ===";
for @results -> %r {
my $bar = "#" x (%r<average> / 5).Int;
say sprintf("%-10s %5.1f %s", %r<name>, %r<average>, $bar);
}
my @averages = @results.map(*<average>);
say "\nClass average: { ([+] @averages) / @averages.elems }";
say "Highest: { @averages.max }";
say "Lowest: { @averages.min }";
What is Next?
With lists and arrays mastered, the natural companion is hashes. Next, we will explore Raku's hash and Pair system for working with key-value data.