Sort::Naturally(3pm)
π NAME π
okay so this is the page for Sort::Naturally (Sort::Naturally(3pm)), from the User Contributed Perl Documentation crew π. It's a sort module that sorts the words lexically but sorts the number bits numerically, so foo9 doesn't get stuck after foo100 π
Sort::Naturally -- sort lexically, but sort numeral parts numerically β¨
πΈ SYNOPSIS πΈ
@them = nsort(qw(
foo12a foo12z foo13a foo 14 9x foo12 fooa foolio Foolio Foo12a
));
print join(' ', @them), "\n";
Prints:
9x 14 foo fooa foolio Foolio foo12 foo12a Foo12a foo12z foo13a
(Or foo12a + Foo12a and foolio + Foolio might be switched, depending on your locale. it's giving locale drama ππ
)
π¦ DESCRIPTION π¦
okay bestie, this module exports two functions, nsort and ncmp. they're how I built my own version of "natural sorting" π§ In natural sorting, numeric chunks get compared as numbers, and everything else that's made of word characters gets compared as text.
This is the vibe I'm going for when I say natural sorting:
- Non-numeric word-character substrings are sorted as text, and it doesn't care about upper or lower case. so
Foolands betweenfishandfowl⨠- Numeric substrings are sorted as numbers, so
100comes after20and not before it π’ \Wsubstrings (neither word characters nor digits) get ignored completely π- Locale matters here.
\w,\d,\D, and\Wall depend on your locale, because Sort::Naturally uses ause localestatement π - When two strings are compared and a number in one isn't lined up against a number in the other, the non-number always goes first. To make that work, a missing number gets treated as the value -1, like this:
foo => "foo", -1
foobar => "foo", -1, "bar"
foo13 => "foo", 13,
foo13xyz => "foo", 13, "xyz"
That's how foo stays in front of foo13, and foo13 stays in front of foobar π
- The start of a string is the exception. Leading non-
\Wpieces (not word characters and not digits) get ignored, and numbers come before letters there π - What counts as a "numeric substring"? Just runs that match
m/\d+/. Scientific notation, commas, decimals, and all that jazz don't get detected. If your data has thousands separators like"20,000 Leagues Under The Sea"or"20.000 lieues sous les mers", strip them out before you feed things intonsortorncmpπ«Ά
β¨ The nsort function β¨
This function takes a list of strings and gives you back a sorted copy of that list.
This is the one most people will want:
@stuff = nsort(...list...);
When nsort needs to compare non-numeric substrings, it uses Perl's lc function inside a <use locale> scope. And when nsort needs to lowercase stuff, it also uses Perl's lc function inside a <use locale> scope. If you'd rather nsort used other functions, you can pass them in an arrayref as the first argument to nsort:
@stuff = nsort( [
\&string_comparator, # optional
\&lowercaser_function # optional
],
...list...
);
If you only want a custom string comparator and no lowercaser, your options list is [\&comparator, ''] or [\&comparator]. If you want a lowercaser but no custom comparator, your options list is ['', \&lowercaser].
Any comparator you pass in gets called as $comparator->($left, $right). Just like a normal Perl cmp replacement, it has to return -1, 0, or 1, depending on whether the left argument is stringwise less than, equal to, or greater than the right one π§
Any lowercaser you pass in gets called as $lowercased = $lowercaser->($original).
β οΈ bestie, the lowercaser must not modify its $_[0]. Don't touch the original, it's not worth the drama π
π The ncmp function π
Sometimes you're sorting non-string values, like this:
@objects_sorted = sort { $a->tag cmp $b->tag } @objects;
...or maybe you're doing a Schwartzian transform, like this:
@strings =
map $_->[0]
sort { $a->[1] cmp $b->[1] }
map { [$_, make_a_sort_key_from($_) ]
@_
;
...and in those cases you might want something that replaces cmp, not sort. That's what the ncmp function from Sort::Naturally is for. You call it as ncmp($left,$right) instead of $left cmp $right, and otherwise it's a totally fine swap-in:
@objects_sorted = sort { ncmp($a->tag,$b->tag) } @objects;
@strings =
map $_->[0]
sort { ncmp($a->[1], $b->[1]) }
map { [$_, make_a_sort_key_from($_) ]
@_
;
Just like nsort, ncmp can take a different string comparator and/or lowercaser. Pass an arrayref as the first argument:
ncmp( [
\&string_comparator, # optional
\&lowercaser_function # optional
],
$left, $right
)
You might be able to get string comparators from Sort::ArbBiLex π¦
π NOTES π
- This module is not a substitute for Sort::Versions! If you just need proper version sorting, go use that, bestie π
- If you need something that kinda works like this module's functions but not quite the same, consider scrolling through this module's source code and adapting what you find. Past the POD, there are several alternate attempts I made at natural sorting routines. They're not compiled as part of the module, but you might find them useful. They should all be working implementations of slightly different algorithms (all based on Martin Pool's "nsort"), which I eventually ditched in favor of my own algorithm. If you're naturally sorting very large data sets and sorting is getting ridiculously slow, you could try one of those discarded functions. I have a feeling they might be faster on large data sets. Benchmark them on your data and see π (Unless you really need the speed, don't bother. Hint: swap
sortin fornsortin your code. If your program doesn't speed up drastically, the sorting isn't the problem. But if it isnsortslowing things down, consider something like this:)
if(@set >= SOME_VERY_BIG_NUMBER) {
no locale; # vroom vroom
@sorted = sort(@set); # feh, good enough
} elsif(@set >= SOME_BIG_NUMBER) {
use locale;
@sorted = sort(@set); # feh, good enough
} else {
# but keep it pretty for normal cases
@sorted = nsort(@set);
}
- If you do adapt the routines in this module, email me! I'd genuinely love to hear about it π₯Ή
- Thanks to the EFNet #perl people for encouraging this module, especially magister and a-mused π«Ά
π COPYRIGHT AND DISCLAIMER π
Copyright 2001, Sean M. Burke sburke@cpan.org, all rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose.
π AUTHOR π
Sean M. Burke sburke@cpan.org π
okay that's the whole tea π this page came from perl v5.36.0, last updated 2022-11-19, so go be a sorting legend, bestie π§β¨π¦
π¦ where this page comes from
- debian bookworm: libsort-naturally-perl 1.03-4 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- debian trixie: libsort-naturally-perl 1.03-4 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- ubuntu noble: libsort-naturally-perl 1.03-4 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- ubuntu questing: libsort-naturally-perl 1.03-4 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- ubuntu resolute: libsort-naturally-perl 1.03-4 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- ubuntu stonking: libsort-naturally-perl 1.03-4 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- π girlified by anthropic/claude-haiku-5.5 on : a language model's rewrite of the original, under its terms