Contributed by Russ
on Jul 05, 2000 at 12:35 UTC
Q&A
> sorting
Description: I need to sort a list of strings which are network line speeds,
like: "115kbps", "2Mbps", "10Mbps", "T1 LINE", ...
If I do the standard (ASCIIbetical) sort, "10Mbps" would come before "115kbps", and "115kbps" would come before "2Mbps", which is clearly not the result I want.
So I need some trick to make a "priority hash" or
array where I'd put the proper order of sort.
I understand how to make comparisons in the
subroutine:
return 1; # moves the item lower
return +1; # goes above the previous one
return 0; # equals
Answer: How do I write my own sorting routine? contributed by jarich If you have a hash which specifies an ordering for values, then your sorting is much easier:
my %hash = (
"very slow" => 1,
"slow" => 2,
"slowish" => 3,
"medium" => 4,
"acceptable" => 5,
"fast" => 6,
"very fast" => 7,
);
Now to sort, we just compare the array elements as keys into the hash:
my @sorted = sort { $hash{$a} <=> $hash{$b} } @array;
Hence:
my @array = ("slow", "very fast", "fast", "very slow", "acceptable");
would sort into:
very slow, slow, acceptable, fast, very fast
 Answer: How do I write my own sorting routine? contributed by nardo return 1 means $a goes before $b, return +1 means $b goes before $a, return 0 means they are equal.
The "spaceship operator", <=>, returns 1, 0, or 1 depending on whether the left argument is numerically less than, equal to, or greater than the right argument.
Its stringwise counterpart, cmp, returns 1, 0, or 1 depending on whether the left argument is stringwise less than, equal to, or greater than the right argument.
To learn more about these operators, please read perlop.
To learn more about the sort function, please read perlfunc.
 Answer: How do I write my own sorting routine? contributed by gam3 This seems like it is begging for a schwartzian transform.
my %engr_scale = (
k => 10**3,
m => 10**6,
g => 10**9,
t => 10**12,
);
sub normalized_net_speed {
local $_ = lc shift;
s/^T1\b/1.544 mbps/i;
s/^([.09]+)\s*([kmgt])bps/ int( $1 * $engr_scale{lc $2} ) /e;
s/[^.09].*$//; # kill nonnumeric stuff
$_
}
my @sorted =
map { $_>[0] }
sort { $a>[1] <=> $b>[1] }
map { [ $_, normalized_net_speed($_) ] }
@array;
 Answer: How do I write my own sorting routine? contributed by poolpi #!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;
use Math::BigInt lib => 'GMP';
my @speeds =
( '10Mbps', '115kbps', 'Flash', '2Mbps', 'T1 LINE', 'Shocker' );
my ( @clean_speeds, @sorted_speeds );
my $speed_re = {
'BPS' => qr/(\d+)\s*([KMGT])BPS/, # 10MBPS
'T' => qr/T(\d)\s*\w*/, # T1 LINE
};
my $bps = Math::BigInt>new('2')>bpow(10);
my $unit = {
'BPS' => {
K => $bps>copy>bpow(0),
M => $bps>copy>bpow(1),
G => $bps>copy>bpow(2),
T => $bps>copy>bpow(3),
},
# ref => http://ckp.madeit.com/t1234.html
'T' => {
1 => Math::BigInt>new('1544'), # T1 = 1544 kbps
2 => Math::BigInt>new('6312'), # T2 = 6312 kbps
3 => Math::BigInt>new('44736'), # T3 = 44736 kbps
4 => Math::BigInt>new('274760'), # T4 = 274760 kbps
},
};
for (@speeds) {
my $speed = uc $_;
$speed =~ /(?:$speed_re>{'BPS'}$speed_re>{'T'})/
? push @clean_speeds, $speed
: warn "Unmatched speed: $speed\n";
}
@sorted_speeds =
sort { net_speed($a) <=> net_speed($b) } @clean_speeds;
print Dumper \@sorted_speeds;
sub net_speed {
my $speed = shift;
if ( $speed =~ /$speed_re>{'BPS'}/ ) {
Math::BigInt>new( $unit>{'BPS'}>{$2} )>bmul($1);
}
elsif ( $speed =~ /$speed_re>{'T'}/ ) {
Math::BigInt>new( $unit>{'T'}>{$1} );
}
}
__END__
Output:

Unmatched speed: FLASH
Unmatched speed: SHOCKER
$VAR1 = [
'115KBPS',
'T1 LINE',
'2MBPS',
'10MBPS'
];

Please (register and) log in if you wish to add an answer
Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
Read Where should I post X? if you're not absolutely sure you're posting in the right place.
Please read these before you post! —
Posts may use any of the Perl Monks Approved HTML tags:
 a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)

For: 

Use: 
 &   & 
 <   < 
 >   > 
 [   [ 
 ]   ] 
Link using PerlMonks shortcuts! What shortcuts can I use for linking?
See Writeup Formatting Tips and other pages linked from there for more info.

