NAME

Sort::BySpec - Sort array (or create a list sorter) according to specification

VERSION

This document describes version 0.040 of Sort::BySpec (from Perl distribution Sort-BySpec), released on 2021-05-01.

SYNOPSIS

use Sort::BySpec qw(sort_by_spec cmp_by_spec);

my $sorter = sort_by_spec(spec => [
    # put odd numbers first, in ascending order
    qr/[13579]\z/ => sub { $_[0] <=> $_[1] },

    # then put specific numbers here, in this order
    4, 2, 42,

    # put even numbers last, in descending order
    sub { $_[0] % 2 == 0 } => sub { $_[1] <=> $_[0] },
]);

my @res = $sorter->(1..15, 42);
# => (1,3,5,7,9,11,13,15,  4,2,42,   14,12,10,8,6)

DESCRIPTION

This package provides a more powerful alternative to Sort::ByExample. Unlike in `Sort::ByExample` where you only provide a single array of example, you can specify multiple examples as well as regex or matcher subroutine coupled with sort rules. With this, you can more precisely specify how elements of your list should be ordered. If your needs are not met by Sort::ByExample, you might want to consider this package. The downside is performance penalty, especially when your list is large.

To sort using Sort::BySpec, you provide a "spec" which is an array of strings, regexes, or coderefs to match against elements of your list to be sorted. In the simplest form, the spec contains only a list of examples:

my $sorter = sort_by_spec(spec => ["foo", "bar", "baz"]); # [1]

and this is equivalent to Sort::ByExample:

my $sorter = sbe(["foo", "bar", "baz"]);

You can also specify regex to match elements. This is evaluated after strings, so this work:

my $sorter = sort_by_spec(spec => [qr/o/, "foo", "bar", "baz", qr/a/]);
my @list = ("foo", "food", "bar", "back", "baz", "fool", "boat");
my @res = $sorter->(@list);
# => ("food","boat","fool",   "foo","bar","baz",   "back")

Right after a regex, you can optionally specify a sort subroutine to tell how to sort elements matching that regex, for example:

my $sorter = sort_by_spec(spec => [
    qr/o/ => sub { $_[0] cmp $_[1] },
    "foo", "bar", "baz",
    qr/a/
]);

# the same list @list above will now be sorted into:
# => ("boat","food","fool",   "foo","bar","baz",   "back")

Note that instead of $a and $b, you should use $_[0] and $_[1] respectively. This avoids the package scoping issue of $a and $b, making your sorter subroutine works everywhere without any special workaround.

Finally, aside from strings and regexes, you can also specify a coderef matcher for more complex matching. Just like in the case of regex, right after the coderef you can optionally specify a sort subroutine (another coderef) to tell how to sort matching elements. For example:

my $sorter = sort_by_spec(spec => [
    # put odd numbers first, in ascending order
    sub { $_[0] % 2 } => sub { $_[0] <=> $_[1] },

    # then put specific numbers here, in this order
    4, 2, 42,

    # put even numbers last, in descending order
    sub { $_[0] % 2 == 0 } => sub { $_[1] <=> $_[0] },
]);

my @res = $sorter->(1..15, 42);
# => (1,3,5,7,9,11,13,15,  4,2,42,   14,12,10,8,6)

FUNCTIONS

cmp_by_spec

Usage:

cmp_by_spec(%args) -> code

Create a compare subroutine to be used in sort().

This function is not exported by default, but exportable.

Arguments ('*' denotes required arguments):

  • reverse => bool

    If set to true, will reverse the sort order.

  • spec* => array

  • xform => code

    Code to return sort keys from data elements.

    This is just like xform in Sort::ByExample.

Return value: (code)

sort_by_spec

Usage:

sort_by_spec(%args) -> array|code

Sort array (or create a list sorter) according to specification.

Examples:

  • Sort according to a sequence of scalars (like Sort::ByExample):

    sort_by_spec(
      spec  => ["foo", "bar", "baz"],
      array => [1, 2, 3, "bar", "a", "b", "c", "baz"]
    );

    Result:

    ["bar", "baz", 1, 2, 3, "a", "b", "c"]
  • Like previous example, but reversed:

    sort_by_spec(
      spec    => ["foo", "bar", "baz"],
      array   => [1, 2, 3, "bar", "a", "b", "c", "baz"],
      reverse => 1
    );

    Result:

    ["bar", "baz", 1, 2, 3, "a", "b", "c"]
  • Put integers first (in descending order), then a sequence of scalars, then others (in ascending order):

    sort_by_spec(
      spec  => [
                 qr/\A\d+\z/,
                 sub { $_[1] <=> $_[0] },
                 "foo",
                 "bar",
                 "baz",
                 qr//,
                 sub { $_[0] cmp $_[1] },
               ],
      array => ["qux", "b", "a", "bar", "foo", 1, 10, 2]
    );

    Result:

    [10, 2, 1, "foo", "bar", "a", "b", "qux"]

This function is not exported by default, but exportable.

Arguments ('*' denotes required arguments):

  • array => array

  • reverse => bool

    If set to true, will reverse the sort order.

  • spec* => array

  • xform => code

    Code to return sort keys from data elements.

    This is just like xform in Sort::ByExample.

Return value: Sorted array, or sort coderef (array|code)

If array is specified, will returned the sorted array. If array is not specified in the argument, will return a sort subroutine that can be used to sort a list and return the sorted list.

HOMEPAGE

Please visit the project's homepage at https://metacpan.org/release/Sort-BySpec.

SOURCE

Source repository is at https://github.com/perlancar/perl-Sort-BySpec.

BUGS

Please report any bugs or feature requests on the bugtracker website https://rt.cpan.org/Public/Dist/Display.html?Name=Sort-BySpec

When submitting a bug or request, please include a test-file or a patch to an existing test-file that illustrates the bug or desired feature.

SEE ALSO

Sort::ByExample

Bencher::Scenario::SortBySpec

AUTHOR

perlancar <perlancar@cpan.org>

COPYRIGHT AND LICENSE

This software is copyright (c) 2021, 2017, 2015 by perlancar@cpan.org.

This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.