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
inSort::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
inSort::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
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.