Björn Höhrmann

NAME

List::OrderBy - Multi-key sorting using order_by and then_by

SYNOPSIS

  use List::OrderBy;
  my @sorted = order_by { ... }
                then_by { ... }
                then_by { ... } @unsorted;

DESCRIPTION

Routines to generate ordered lists using key extraction code blocks with support for multi-key sorting.

ROUTINES

order_by { ... } @list
order_by \&code, @list

The main routine takes a code block or subroutine reference and a list, applies the specified code to every element in the list to extract a sorting key, and then returns a list ordered according to the extracted keys, using sort and `<=>` internally. In the code block the list item value is available as $_, and subroutines are additionally called with the value as first parameter.

  my @sorted = order_by { length }
    qw/xxx xx x/; # returns qw/x xx xxx/
then_by { ... } @list

In a chain starting with order_by, then_by specifies an additional ordering key extractor. The extracted key will be used to order elements if keys extracted by preceding order_by or then_by calls are equivalent.

  my @sorted = order_by { $_->width  }
                then_by { $_->height } @shapes;

This would first sort elements by their width and then by their height.

order_by_desc { ... } @list

Same as order_by but uses descending order.

order_cmp_by { ... } @list

Same as order_by but uses cmp to compare extracted keys.

order_cmp_by_desc { ... } @list
then_cmp_by { ... } @list
then_by_desc { ... } @list
then_cmp_by_desc { ... } @list

Analogous to the similarily named routines.

EXPORTS

The functions order_by, then_by, order_cmp_by, then_cmp_by, order_by_desc, then_by_desc, order_cmp_by_desc, and then_cmp_by_desc, by default.

KNOWN ISSUES

This module is mainly an experiment to see how Schwartzian transforms can be avoided in code, considering the pattern can be difficult to read, and it becomes unmaintainable with multiple keys. There are a number of issues though, like how to manage side-effects: should the module call a secondary key extractor even when the key is not actually needed? Should the module ensure that the key extractor is called only once? Does the ordering between calls to the key extractors matter?

Another problem is of course naming, order_by { ... } then_by { ... } is nice enough, but there does not seem to be a good way to add options like the comparison operator or ascending/descending behavior. Same for the side-effects question above if that was to be made configurable. A syntax with named parameters like in order_by :cmp :desc { ... } would be better but is not yet available with Perl5.

One gotcha I've noticed is with sorting strings by length. Since they are strings, you might be inclined to use a cmp variant, but order_cmp_by { length } usually is not what authors want. In a draft version of this module I actually called the routine order_strings_by, and switched to order_cmp_by to make it less misleading.

AUTHOR / COPYRIGHT / LICENSE

  Copyright (c) 2013 Bjoern Hoehrmann <bjoern@hoehrmann.de>.
  This module is licensed under the same terms as Perl itself.