Spaceship Operator
This page explains how to sort lists in Perl using the spaceship operator. You will reach for it whenever you need to put a collection in order. The operator is just three characters:The spaceship operator. Three characters that tell you how two numbers relate to each other.<=>
This is a three-way comparison. It returns -1, 0, or 1, which is exactly what5 <=> 10 # -1 (less than) 10 <=> 5 # 1 (greater than) 7 <=> 7 # 0 (equal)
sort expects.
Part 1: THE BASICS
The spaceship operator compares two numbers and returns:That output matches what-1 if left is less than right 0 if they're equal 1 if left is greater than right
sort expects. You pass a block to sort that uses $a and $b, and Perl handles the rest.
If you skip the operator, Perl falls back to string sorting. Numbers then compare by their first character instead of their value.my @nums = (5, 2, 9, 1, 7); my @sorted = sort { $a <=> $b } @nums; # (1, 2, 5, 7, 9)
my @wrong = sort @nums; # (1, 2, 5, 7, 9) - looks right... my @nums = (5, 2, 19, 1, 7); my @wrong = sort @nums; # (1, 19, 2, 5, 7) - WRONG! String "19" < "2"
Part 2: ASCENDING VS DESCENDING
You reverse the order by swapping$a and $b in the sort block.
The trick is remembering which is which. You can think of it like subtraction.# Ascending (smallest first) my @asc = sort { $a <=> $b } @nums; # Descending (largest first) my @desc = sort { $b <=> $a } @nums;
$a <=> $b # $a - $b: positive means a is bigger, goes later $b <=> $a # $b - $a: positive means b is bigger, b goes later .--. |o_o | |:_/ | // \ \ (| | ) /'\_ _/`\ \___)=(___/
Part 3: THE STRING VERSION
The spaceship operator handles numbers. For strings, Perl provides cmp:It returns the same three values, but it compares text instead of numbers.'apple' cmp 'banana' # -1 (a before b) 'banana' cmp 'apple' # 1 (b after a) 'apple' cmp 'apple' # 0 (equal)
You can also skip the block for simple string sorting.my @words = qw(cherry apple banana); my @sorted = sort { $a cmp $b } @words; # (apple, banana, cherry)
Perl's default sort uses string-basedmy @sorted = sort @words; # Same result
cmp automatically.
Part 4: MULTI-LEVEL SORTING
The real power shows up in complex sorts. You chain comparisons with||:
Result:my @people = ( { name => 'Alice', age => 30 }, { name => 'Bob', age => 25 }, { name => 'Carol', age => 30 }, ); my @sorted = sort { $a->{age} <=> $b->{age} # First: by age || $a->{name} cmp $b->{name} # Then: by name } @people;
TheBob (25), Alice (30), Carol (30)
|| operator short-circuits. If the age comparison returns 0, Perl falls through to the name comparison.
Part 5: THREE-LEVEL AND BEYOND
You keep chaining for more levels.Each level only matters when the previous levels match.my @sorted = sort { $a->{department} cmp $b->{department} || $b->{salary} <=> $a->{salary} # Descending salary || $a->{hire_date} cmp $b->{hire_date} || $a->{name} cmp $b->{name} } @employees;
Part 6: SORTING HASH KEYS BY VALUE
This is a classic pattern for sorting keys by their corresponding values.The sort block compares values, but you are sorting the keys.my %scores = ( alice => 85, bob => 92, carol => 78, ); # Sort names by score (highest first) my @ranking = sort { $scores{$b} <=> $scores{$a} } keys %scores; # (bob, alice, carol)
Part 7: CUSTOM COMPARISONS
The spaceship operator works with any numeric expression.# Sort by string length my @by_length = sort { length($a) <=> length($b) } @words; # Sort by last character my @by_last = sort { ord(substr($a,-1)) <=> ord(substr($b,-1)) } @words; # Sort IPs numerically my @ips = sort { my @a = split /\./, $a; my @b = split /\./, $b; $a[0] <=> $b[0] || $a[1] <=> $b[1] || $a[2] <=> $b[2] || $a[3] <=> $b[3] } @ip_addresses;
Part 8: THE SCHWARTZIAN TRANSFORM
You use this pattern when sorting requires expensive computation.The transform is named after Randal Schwartz. The spaceship operator handles the middle step.# Slow: computes length every comparison my @sorted = sort { length($a) <=> length($b) } @strings; # Fast: compute once, sort, extract my @sorted = map { $_->[0] } # 3. Extract original sort { $a->[1] <=> $b->[1] } # 2. Sort by cached value map { [ $_, length($_) ] } # 1. Cache expensive calc @strings;
Part 9: GOTCHAS
You will see warnings if your list contains undefined values.You can fix that with defined-or checks.my @nums = (5, undef, 3, undef, 1); my @sorted = sort { $a <=> $b } @nums; # Warnings! undef used in numeric comparison
Non-numeric strings also cause trouble.my @sorted = sort { ($a // 0) <=> ($b // 0) } @nums;
The spaceship operator is for numbers. Know your data.my @mixed = ('5', 'ten', '3'); my @sorted = sort { $a <=> $b } @mixed; # 'ten' becomes 0 numerically, with warnings
Part 10: VS OTHER LANGUAGES
Many languages borrowed the operator from Perl.Python usesRuby: 5 <=> 10 PHP: 5 <=> 10 (as of PHP 7) Groovy: 5 <=> 10
cmp() functions differently.
Perl shipped it first. Version 5 arrived in 1994.
Part 11: THE NAME
Look at the characters again.It looks like a spaceship viewed from above. The<=>
< and > are wings, and the = is the body.
You might need to lean into the shape a bit. The name stuck anyway, and every language that copied it uses the same label.<=> / \ < = > \ /
perl.gg<=> / \ < = > \ / / \ / \ Three-way comparison since 1994