Your bucketsort is the elegant solution that I was seeking. Interesting though it's about twice as slow as my ugly version as indicated by this benchmark:
That's because you didn't fix my typos. Here's a different
benchmark, with some of the other suggestions (I didn't include any that went beyond sorting on 'fld_type'):
#!/usr/bin/perl
use strict;
use warnings;
use Benchmark qw /timethese cmpthese/;
our @advocates = map {{fld_title => 'Rec' . $_,
fld_type => [qw /National State Local/] -> [ra
+nd 3]}}
1 .. shift || 1000;
sub by_locale {
my $a_type;
my $b_type;
$a_type = 1 if ($a->{'fld_type'} =~ /local/i);
$a_type = 2 if ($a->{'fld_type'} =~ /state/i);
$a_type = 3 if ($a->{'fld_type'} =~ /national/i);
$b_type = 1 if ($b->{'fld_type'} =~ /local/i);
$b_type = 2 if ($b->{'fld_type'} =~ /state/i);
$b_type = 3 if ($b->{'fld_type'} =~ /national/i);
$a_type <=> $b_type;
}
my $order = "\0local\0state\0national\0";
sub by_locale_b {
index ($order, "\0" . lc ($a -> {fld_type}) . "\0") <=>
index ($order, "\0" . lc ($b -> {fld_type}) . "\0")
}
our (@knowmad, @abigail, @borisz, @pirate);
cmpthese -1 => {
knowmad => '@knowmad = sort by_locale @advocates',
abigail => 'my %bucket;
map {push @{$bucket {$_ -> {fld_type}}} => $_} @advo
+cates;
@abigail = map {@{$bucket {$_}}} qw /Local State Nat
+ional/;',
borisz => 'my %h = (National => 3, State => 2, Local => 1);
@borisz = sort {$h {$a -> {fld_type}} <=>
$h {$b -> {fld_type}}} @advocates;',
pirate => '@pirate = map {$_ -> [1]}
sort {$a -> [0] <=> $b -> [0]}
map {[lc ($_ -> {fld_type}), $_]} @advoca
+tes',
browseruk => '@browseruk = sort by_locale_b @advocates',
};
__END__
Rate knowmad browseruk borisz pirate abigail
knowmad 39.4/s -- -48% -70% -83% -92%
browseruk 75.5/s 91% -- -42% -67% -85%
borisz 130/s 229% 72% -- -43% -74%
pirate 229/s 480% 203% 76% -- -53%
abigail 491/s 1147% 551% 279% 115% --
Abigail