Perl 如何按一个哈希值对哈希引用数组进行排序?

Perl 如何按一个哈希值对哈希引用数组进行排序?,perl,sorting,bugzilla,Perl,Sorting,Bugzilla,首先,请原谅我生锈的Perl。我试图修改Bugzilla的“whine.pl”,以生成按严重程度排序的bug列表 所以它给了我一个散列引用数组。每个散列包含一组关于特定bug的信息(id、受让人、严重性等)。我想按严重性对数组进行排序。最好的方法是什么 我会想出几个可能性。一种是创建五个数组(每个严重性级别一个),然后在该数组上循环,并将哈希引用推送到相应的严重性级别数组中。在这之后,我可以重新组装它们并用排序后的数组替换原始数组 我朋友提出的另一种方法是将严重性级别(存储为散列中的文本)分配给

首先,请原谅我生锈的Perl。我试图修改Bugzilla的“whine.pl”,以生成按严重程度排序的bug列表

所以它给了我一个散列引用数组。每个散列包含一组关于特定bug的信息(id、受让人、严重性等)。我想按严重性对数组进行排序。最好的方法是什么

我会想出几个可能性。一种是创建五个数组(每个严重性级别一个),然后在该数组上循环,并将哈希引用推送到相应的严重性级别数组中。在这之后,我可以重新组装它们并用排序后的数组替换原始数组

我朋友提出的另一种方法是将严重性级别(存储为散列中的文本)分配给一些numer,然后对它们进行cmp。也许是这样的

sub getVal {
    my $entry = $_[0];
    %lookup = ( "critical" => 0, ... );
    return $lookup(entry("bug_severity"));
}
@sorted = sort { getVal($a) <=> getVal($b) } @unsorted;
sub-getVal{
我的$entry=$\u0];
%查找=(“关键”=>0,…);
返回$lookup(条目(“bug_严重性”);
}
@排序=排序{getVal($a)getVal($b)}@unsorted;

我喜欢您提出的解决方案:

my %sevs = (critical => 0, high => 1, ...);
my @sorted = sort { $sevs{$a->{bug_severity}} <=> $sevs{$b->{bug_severity}} } @unsorted
my%sevs=(临界=>0,高=>1,…);
my@sorted=sort{$sevs{$a->{bug_severity}}$sevs{$b->{bug_severity}}}@unsorted

为了避免调用getVal的次数过多,可以使用“装饰、排序、取消装饰”。装潢是获取您真正关心的排序信息:

my @decorated = map { [ $_, getVal($_) ] } @unsorted;
然后对装饰列表进行排序:

my @sortedDecorate = sort { $a->[1] <=> $b->[1] } @decorated;
或者更像Perl的方法:

@sorted = map { $_->[0] }
          sort { $a->[1] <=> $b->[1] }
          map { [ $_, getVal($_) ] } @unsorted;
@sorted=map{$\->[0]}
排序{$a->[1]$b->[1]}
映射{[$\uz,getVal($\u]}@unsorted;
您可以使用:

将每个bug映射到一个数组引用,该数组引用的第一个元素是查找表中的数值bug严重程度。使用Schwartzian变换,
@unsorted
中为每个bug只查找一次值

那么

sort { $a->[0] <=> $b->[0] }
从排序返回的数组中提取原始bug

当它所做的只是散列查找时,实际上不需要
getval

对于自动生成高效分拣机,CPAN模块非常优秀:

use strict; use warnings;

use Sort::Maker;

my @bugs = (
    { name => 'bar', bug_severity => 'severe' },
    { name => 'baz', bug_severity => 'noncritical' },
    { name => 'foo', bug_severity => 'critical' },
);

my $sorter = make_sorter('ST',
    name      => 'severity_sorter',
    init_code => 'my %lookup = (
                     critical => 0,
                     severe => 1,
                     noncritical => -1 );',
    number    => [ code => '$lookup{$_->{bug_severity}}' ],
);

use Data::Dumper;
print Dumper $_ for severity_sorter( @bugs );
输出:

$VAR1 = { 'name' => 'baz', 'bug_severity' => 'noncritical' }; $VAR1 = { 'name' => 'foo', 'bug_severity' => 'critical' }; $VAR1 = { 'name' => 'bar', 'bug_severity' => 'severe' }; C:\Temp> tzt 10 It took 38 lookups to sort 10 elements C:\Temp> tzt 100 It took 978 lookups to sort 100 elements C:\Temp> tzt 1000 It took 10916 lookups to sort 1000 elements C:\Temp> tzt 10000 It took 113000 lookups to sort 10000 elements C:\Temp> tzt 10 Rate schwartzian naive schwartzian 18842/s -- -29% naive 26357/s 40% -- C:\Temp> tzt 100 Rate naive schwartzian naive 1365/s -- -11% schwartzian 1532/s 12% -- C:\Temp> tzt 1000 Rate naive schwartzian naive 121/s -- -11% schwartzian 135/s 12% -- 输出:

$VAR1 = { 'name' => 'baz', 'bug_severity' => 'noncritical' }; $VAR1 = { 'name' => 'foo', 'bug_severity' => 'critical' }; $VAR1 = { 'name' => 'bar', 'bug_severity' => 'severe' }; C:\Temp> tzt 10 It took 38 lookups to sort 10 elements C:\Temp> tzt 100 It took 978 lookups to sort 100 elements C:\Temp> tzt 1000 It took 10916 lookups to sort 1000 elements C:\Temp> tzt 10000 It took 113000 lookups to sort 10000 elements C:\Temp> tzt 10 Rate schwartzian naive schwartzian 18842/s -- -29% naive 26357/s 40% -- C:\Temp> tzt 100 Rate naive schwartzian naive 1365/s -- -11% schwartzian 1532/s 12% -- C:\Temp> tzt 1000 Rate naive schwartzian naive 121/s -- -11% schwartzian 135/s 12% -- C:\Temp>tzt 10 对10个元素进行排序需要38次查找 C:\Temp>tzt 100 对100个元素进行排序需要978次查找 C:\Temp>tzt 1000 对1000个元素进行排序需要10916次查找 C:\Temp>tzt 10000 对10000个元素进行排序需要113000次查找 因此,我们需要更多的信息来决定使用朴素排序还是使用施瓦茨变换是合适的解决方案

这里有一个简单的基准,它似乎与@Ether的观点一致:

#!/usr/bin/perl

use strict;
use warnings;

use Benchmark qw( cmpthese );

my ($n_elements) = @ARGV;

my @keys = qw(foo bar baz);
my %lookup = map { $keys[$_] => $_ } 0 .. $#keys;

my @unsorted = map { {v => $keys[rand 3]} } 1 .. $n_elements;

cmpthese(-1, {
    naive => sub {
        my @sorted = sort {
            $lookup{$a->{v}} <=> $lookup{$b->{v}}
        } @unsorted;
    },
    schwartzian => sub {
        my @sorted = map  { $_->[1] }
                     sort { $a->[0] <=> $b->[0] }
                     map  { [$lookup{$_->{v}}, $_] }
                     @unsorted;
    }
});
#/usr/bin/perl
严格使用;
使用警告;
使用基准qw(CMP准则);
my($n_元素)=@ARGV;
my@keys=qw(foo-bar-baz);
我的%lookup=map{$keys[$\u]=>$\u0$#钥匙;
my@unsorted=map{{v=>$keys[rand3]}}1$n_元素;
cmpthese(-1{
天真=>sub{
我的@sorted=排序{
$lookup{$a->{v}}$lookup{$b->{v}
}@未分类;
},
schwartzian=>sub{
我的@sorted=map{$\u->[1]}
排序{$a->[0]$b->[0]}
映射{[$lookup{$\->{v},$\]}
@未分类;
}
});
输出:

$VAR1 = { 'name' => 'baz', 'bug_severity' => 'noncritical' }; $VAR1 = { 'name' => 'foo', 'bug_severity' => 'critical' }; $VAR1 = { 'name' => 'bar', 'bug_severity' => 'severe' }; C:\Temp> tzt 10 It took 38 lookups to sort 10 elements C:\Temp> tzt 100 It took 978 lookups to sort 100 elements C:\Temp> tzt 1000 It took 10916 lookups to sort 1000 elements C:\Temp> tzt 10000 It took 113000 lookups to sort 10000 elements C:\Temp> tzt 10 Rate schwartzian naive schwartzian 18842/s -- -29% naive 26357/s 40% -- C:\Temp> tzt 100 Rate naive schwartzian naive 1365/s -- -11% schwartzian 1532/s 12% -- C:\Temp> tzt 1000 Rate naive schwartzian naive 121/s -- -11% schwartzian 135/s 12% -- C:\Temp>tzt 10 施瓦茨天真率 施瓦茨18842/s--29% 幼稚26357/s 40%-- C:\Temp>tzt 100 比率天真施瓦茨 幼稚的1365/s--11% 施瓦茨1532/s 12%-- C:\Temp>tzt 1000 比率天真施瓦茨 天真的121/s--11% 施瓦茨135/s 12%--
您可以使用查找表来确定bugzilla严重性的顺序,如下所示(使用示例数据来说明):


还有一个有趣的想法。我喜欢。(但不要使用最后一种方法,perl已经很难理解!)这确实是Schwartzian变换。以我命名,但不是以我命名的。我记得你提到我在教perl课程时,Randal。我仍然很感兴趣的是,社区使用了这个词,而不是一般的装饰类的不装饰@西南,我不是说不要这样做。我是说不要把它变成一行,因为它让人很难理解。“一行”并不比将其分成3行容易理解和阅读的行更有效。Jamessan已经发布了这篇文章,而且几乎不可能不费吹灰之力就能理解它。另一个解释得很清楚的例子:)谢谢你提供的详细信息。我在这里有很多要尝试的东西……这和你在问题中发布的内容(doh,没有仔细阅读)以及tster所说的差不多。因此,是的,我同意这是最好的解决方案我很感激能把我模糊的想法给我解释清楚;感谢您提供的详细示例,为我节省了一些“啊,Perl是如何再次做到这一点的?”时间。查找表非常短(bugzilla示例中只有5个条目),2个。在Schwartzian变换中,您必须多次处理输入数据中的每个条目,在本场景中,这将产生大致相等的费用。除非我遗漏了什么,否则只有在输入数据与用于确定排序顺序的表相比相对较小的情况下,转换才会有回报,并且您还必须考虑代码的复杂性(简单的代码比复杂的代码更容易调试)。@Sinan,好吧,这很公平。但是,如果执行排序方法n log(n)次的计算成本超过两个map调用所需的时间,则使用转换的实际收益是。UnixReview文章中的例子涉及拆分;在OP的示例中,数据已经可以很好地解析为干净的数据结构。因此,我认为在这种情况下,我认为转换不会给我们带来任何好处,即使需要处理一大堆bug;很高兴听到你这么说
$VAR1 = [
          {
            'assignee' => 'Carl',
            'id' => 3,
            'severity' => 'EXTREME'
          },
          {
            'assignee' => 'Bob',
            'id' => 1,
            'severity' => 'HIGH'
          },
          {
            'assignee' => 'Anna',
            'id' => 2,
            'severity' => 'LOW'
          }
        ];