How to use a custom comparison function with sort in Perl?
Question
How to use a custom comparison function with sort in Perl?
In Perl, the sort function allows you to provide a custom comparison function to define how elements are ordered. This is extremely useful when you want to sort complex data structures or impose a nonstandard sorting order.
Basic Usage of Custom Comparison Function with sort
The syntax for sort with a custom comparator looks like this:
@sorted = sort { BLOCK or EXPR } @unsorted;
Here, the block is executed to compare two elements, $a and $b. These are special global variables that hold the two elements currently being compared during the sort. Your block should return:
-1(or any negative value) if$ashould come before$b0if they are equal in ordering1(or any positive value) if$ashould come after$b
Unlike some other languages, Perl’s sort expects the comparison function to return a numeric value indicating order. Typically, you use the <=> (numeric) or cmp (string) operators:
$a <=> $b— numeric comparison$a cmp $b— string comparison
Example: Custom Sort by Length of Strings
This example sorts an array of strings by their length, shortest first:
perl -E '
my @words = qw(apple pear banana fig);
my @sorted = sort { length($a) <=> length($b) } @words;
print join(", ", @sorted), "\n";
'
Output:
fig, pear, apple, banana
Here length($a) <=> length($b) compares string lengths numerically.
Key Perl Concepts
$aand$bare magical package globals: They are implicitly set by thesortroutine to the items being compared. You do not declare them. Never assign to them in your comparison block.- Context matters: Your comparator must return a numeric value. Returning a boolean like
$a lt $b(which returns 1 or empty string) is incorrect and leads to buggy sorts. - TMTOWTDI (There’s more than one way to do it): You can implement complicated sorts using custom code, including multi-level sort by multiple criteria.
More Complex Example: Sort a List of Hashrefs by Multiple Keys
perl -E '
my @data = (
{name => "Alice", age => 25},
{name => "Bob", age => 20},
{name => "Carol", age => 25},
);
my @sorted = sort {
$a->{age} <=> $b->{age} # primary sort by age
||
$a->{name} cmp $b->{name} # secondary sort by name
} @data;
for my $person (@sorted) {
say "$person->{name} ($person->{age})";
}
'
Output:
Bob (20)
Alice (25)
Carol (25)
This example shows how to apply multiple comparison steps by chaining them with ||. When ages compare equal, it falls through to compare names alphabetically.
Common Pitfalls
- Don’t assign to
$aor$b: They are special variables set bysort, and modifying them leads to unexpected behavior. - Use numeric
<=>vs stringcmpproperly: Mixing them up can cause wrong sorting results, e.g., strings sorted with<=>will coerce to numbers. - Remember
sortis stable since Perl 5.8: When two elements compare equal, their original order is preserved. - The block is executed many times: Keep the comparison as efficient as possible to avoid performance issues with large lists.
Summary
To use a custom comparison function in Perl’s sort, write a block that compares $a and $b as per your sorting rules, returning a negative, zero, or positive number to indicate order. Use the triple-equals <=> for numerical and cmp for string comparisons. Leverage chaining with || for multi-level sorts. Follow best practices to avoid common mistakes, and you’ll unlock powerful sorting capabilities.
Verified Code
Executed in a sandbox to capture real output. • v5.34.1 • 4ms
(empty)(empty)