data-structures intermediate

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 $a should come before $b
  • 0 if they are equal in ordering
  • 1 (or any positive value) if $a should 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

  • $a and $b are magical package globals: They are implicitly set by the sort routine 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 $a or $b: They are special variables set by sort, and modifying them leads to unexpected behavior.
  • Use numeric <=> vs string cmp properly: Mixing them up can cause wrong sorting results, e.g., strings sorted with <=> will coerce to numbers.
  • Remember sort is 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

Tip: edit code and use “Run (Browser)”. Server runs always execute the published, verified snippet.
STDOUT
(empty)
STDERR
(empty)

Was this helpful?

Related Questions