I have a array with a bunch of names like vlan-1
, vlan100
, vlan34
which do not sort appropriately using Perl's standard sort()
function. Sort::Naturally to the rescue! I didn't want to install an entire module for one sort operation, and require a dependency, so I ripped out just the natural sort function and included that in my script.
sub nsort {
my($cmp, $lc);
return @_ if @_ < 2; # Just to be CLEVER.
my($x, $i); # scratch vars
map
$_->[0],
sort {
# Uses $i as the index variable, $x as the result.
$x = 0;
$i = 1;
while($i < @$a and $i < @$b) {
last if ($x = ($a->[$i] cmp $b->[$i])); # lexicographic
++$i;
last if ($x = ($a->[$i] <=> $b->[$i])); # numeric
++$i;
}
$x || (@$a <=> @$b) || ($a->[0] cmp $b->[0]);
}
map {
my @bit = ($x = defined($_) ? $_ : '');
if($x =~ m/^[+-]?(?=\d|\.\d)\d*(?:\.\d*)?(?:[Ee](?:[+-]?\d+))?\z/s) {
# It's entirely purely numeric, so treat it specially:
push @bit, '', $x;
} else {
# Consume the string.
while(length $x) {
push @bit, ($x =~ s/^(\D+)//s) ? lc($1) : '';
push @bit, ($x =~ s/^(\d+)//s) ? $1 : 0;
}
}
\@bit;
}
@_;
}
This is a slightly more portable version rather than maintaining the Sort::Naturally
dependency.
See also: Natural Sort Part Deux