Perl 按内部哈希值对哈希数组的哈希进行排序

Perl 按内部哈希值对哈希数组的哈希进行排序,perl,sorting,hash,Perl,Sorting,Hash,我很难理解这一点,我敢肯定,这只是一个今天很密集的例子。我的数据结构与此类似: { PDGFRA => [ { "p.N659K" => 22 }, { "p.D842Y" => 11 }, { "p.I843_S847>T" => 9 }, { "p.D842_H845del" => 35 }, { "p.I843_D846delIMHD" => 24 }, { "p.I843_D846del" =&

我很难理解这一点,我敢肯定,这只是一个今天很密集的例子。我的数据结构与此类似:

{
  PDGFRA => [
    { "p.N659K" => 22 },
    { "p.D842Y" => 11 },
    { "p.I843_S847>T" => 9 },
    { "p.D842_H845del" => 35 },
    { "p.I843_D846delIMHD" => 24 },
    { "p.I843_D846del" => 21 },
    { "p.D842V" => 457 },
    { "p.N659Y" => 5 },
    { "p.M844_S847del" => 7 },
    { "p.S566_E571>K" => 8 },
    { "p.S566_E571>R" => 50 },
    { "p.V561D" => 54 },
  ],
}
我想打印出结果,按照散列值进行反向排序(从最大到最小),所以最终我会得到这样的结果

PDGFRA    p.D842V    457
PDGFRA    p.V561D    54
PDGFRA    p.S566_E571>R    50
PDGFRA    p.D842_H845del    35
.
.
.
etc.
我可以随心所欲地打印数据结构,但在打印之前,我似乎不知道如何对数据进行排序。我尝试过这样分类:

for my $gene ( sort keys %matches ) {
    for my $var ( sort { $a->{$var} <=> $b->{$var} } @{$matches{$gene}} {
        print "$var\n";
    }
 }

有人能告诉我按内部哈希值对哈希或哈希数组进行排序的正确方向吗?

假设上面的
$VAR
Data::Dumper::Dumper(\%matches)

如果您不想让数据结构变得更好

for my $gene ( sort keys %matches ) {
    for my $hash ( sort { 
                      my ($akey) = keys %$a;
                      my ($bkey) = keys %$b;
                      $a->{$akey} <=> $b->{$bkey} 
                   } @{$matches{$gene}} ) {
        my ($key) = keys %$hash;
        print "$key => $hash->{$key}\n";
    }
}
my$gene的
(排序键%matches){
对于我的$hash(排序{
我的($akey)=钥匙%$a;
我的($bkey)=钥匙%$b;
$a->{$akey}$b->{$bkey}
}@{$matches{$gene}}){
my($key)=keys%$hash;
打印“$key=>$hash->{$key}\n”;
}
}
它按值(例如:12)而不是键(例如:“p.I843_D846del”)进行排序。我想,因为您使用了一个数字比较,所以您希望按数字值排序;-)

编辑:内循环的固定体

编辑2:

我看你试过。。。如果您保持数据结构不变,这可能是一个更有效的解决方案。。。详情如下:

for my $gene ( sort keys %matches ) {
    print "$_->[0] => $_->[1]\n" for                     # print both key and value
        sort { $a->[1] <=> $b->[1] }                     # sort by value (e.g: 35)
        map { my ($key) = keys %$_; [$key, $_->{$key}] } # e.g:['p.D842_H845del' ,35]
        @{$matches{$gene}};
}  
my$gene的
(排序键%matches){
为#打印“$\>[0]=>$\>[1]\n”;同时打印键和值
排序{$a->[1]$b->[1]}#按值排序(例如:35)
映射{my($key)=keys%$;[$key,$->{$key}]}例如:['p.D842_H845del',35]
@{$matches{$gene}};
}  

但是,我只是修复数据结构


可能只需将“key”(例如:“p.I843_D846del”)和“value”(例如:12)两个值都设置为一致的键名即可。

您的数据结构似乎具有不必要的深度。但这里有一个Schwartzian变换版本,可以根据需要对其进行排序

for my $gene (sort keys %matches) {
    my @sorted = map  { {@$_} }                # Back to hash.
                 sort { $b->[1] <=> $a->[1] }  # Sort.
                 map  { [each %$_] }           # Unpack the 1-key hash.
                 @{$matches{$gene}};
}
my$gene的
(排序键%matches){
我的@sorted=map{{{@$}}}返回到散列。
排序{$b->[1]$a->[1]}排序。
映射{[each%$\]}#解压1键散列。
@{$matches{$gene}};
}

您已经接受了答案,但我想对您的数据结构发表意见。你有:

  • 杂烩
  • 该散列包含一个数组
  • 这些数组包含一个只有一个键的散列
为什么数组只包含一个键的散列?为什么不干脆去掉这个数组呢

$VAR1 = {
      'PDGFRA' =>  {
                      'p.N659K' => 22,
                      'p.D842Y' => 11,
                      'p.I843_S847>T' => 9,
                      'p.D842_H845del' => 35,
                      'p.I843_D846delIMHD' => 24,
                      'p.I843_D846del' => 21,
                      'p.D842V' => 457,
                      'p.N659Y' => 5,
                      'p.M844_S847del' => 7,
                      'p.S566_E571>K' => 8,
                      'p.S566_E571>R' => 50,
                      'p.V561D' => 54,
     };
这将大大简化您的结构,并使查找包含在最内部散列中的值变得更容易。您可以直接访问这些密钥

如果问题是其中一些哈希键可能重复,则可以使该哈希键指向一个值数组:

$VAR1 = {
      'PDGFRA' =>  {
                      'p.N659K' => 22,
                      'p.D842Y' => 11,
                      'p.I843_S847>T' => [
                                            6,
                                            9
                                          ],
                      'p.D842_H845del' => 35,
                      'p.I843_D846delIMHD' => 24,
                      'p.I843_D846del' => 21,
                       ...
请注意,
p.I843_S847
包含
6
9
。您可以简化内部散列的每个值并使其成为对数组的引用,这些数组中的99%可能包含单个值,或者您可以使用命令检测内容是标量还是对数组的引用。无论采用哪种方式,您仍然可以更快地查找,更容易地访问该散列中的键,因此您可以对它们进行排序


由于您熟悉在Perl中使用复杂的数据结构,因此还应该了解其工作原理。这将使处理这些结构变得更加容易,也将帮助您进行开发,因为它为您提供了一种与这些复杂结构关联的干净方法。

这里有一种替代方法。这个方法的工作原理是将索引列表排序到数组
@sorted
,这样您就可以在数组切片
@$list[@sorted]
上进行迭代

但是,您会受到数据格式的限制,并且仅在给定单个元素散列的情况下提取一对值是很麻烦的

我希望这有帮助

use strict;
use warnings;

my %matches = (
  PDGFRA => [
    { "p.N659K"            =>  22 },
    { "p.D842Y"            =>  11 },
    { "p.I843_S847>T"      =>   9 },
    { "p.D842_H845del"     =>  35 },
    { "p.I843_D846delIMHD" =>  24 },
    { "p.I843_D846del"     =>  21 },
    { "p.D842V"            => 457 },
    { "p.N659Y"            =>   5 },
    { "p.M844_S847del"     =>   7 },
    { "p.S566_E571>K"      =>   8 },
    { "p.S566_E571>R"      =>  50 },
    { "p.V561D"            =>  54 },
  ],
);

while (my ($key, $list) = each %matches) {

  my @sorted = sort {
    my ($ka, $va) = %{ $list->[$b] };
    my ($kb, $vb) = %{ $list->[$a] };
    $va <=> $vb;
  } 0 .. $#$list;

  for my $item (@$list[@sorted]) {
    printf "%s   %s     %d\n", $key, %$item;
  }
}

更新

在我对David W的回答的评论中,我建议使用一种数据结构,它只是由两个元素数组组成的数组,而不是由单个元素散列组成的数组

这就是它的样子。输出与上述代码相同

use strict;
use warnings;

my %matches = (
  PDGFRA => [
    [ "p.N659K",             22 ],
    [ "p.D842Y",             11 ],
    [ "p.I843_S847>T",        9 ],
    [ "p.D842_H845del",      35 ],
    [ "p.I843_D846delIMHD",  24 ],
    [ "p.I843_D846del",      21 ],
    [ "p.D842V",            457 ],
    [ "p.N659Y",              5 ],
    [ "p.M844_S847del",       7 ],
    [ "p.S566_E571>K",        8 ],
    [ "p.S566_E571>R",       50 ],
    [ "p.V561D",             54 ],
  ],
);

while (my ($key, $list) = each %matches) {

  my @sorted = sort { $list->[$b][1] <=> $list->[$a][1] } 0 .. $#$list;

  for my $item (@$list[@sorted]) {
    printf "%s   %s     %d\n", $key, @$item;
  }
}
使用严格;
使用警告;
我的%matches=(
PDGFRA=>[
[“p.N659K”,22],
[“p.D842Y”,11],
[“p.I843_S847>T”,9],
[“p.D842_H845del”,第35页],
[“p.I843_D846delIMHD”,24],
[“p.I843_D846del”,第21页],
[“p.D842V”,457],
[“p.N659Y”,5],
[“p.M844_S847; del”,7],
[“p.S566_E571>K”,8],
[“p.S566_E571>R”,第50页],
[“p.V561D”,54],
],
);
while(my($key,$list)=每个%匹配){
my@sorted=sort{$list->[$b][1]$list->[$a][1]}0..$#$list;
对于我的$item(@$list[@sorted]){
printf“%s%s%d\n”,$key,@$item;
}
}

数组ref中的所有哈希ref只包含一个键?但是每个hash ref的键都不同?这只是一个奇怪的数据结构,不太利于遍历(或排序)。。。虽然很容易处理。是的,我想你可能是对的,我可能会考虑不同的数据结构。我使用键来获取计数数据(这是一个更大、更复杂的数据集)。但这种肯定不好看。我更改了代码中的排序顺序,从最大到最小,在
@{$matches{$gene}}
前面缺少一个括号,但是代码在其他方面运行得非常好!不确定是否有更好的方法来处理这些数据,但我认为现在应该可以了。非常感谢你!刚刚看到了ST方法。事实上,这更好了一点。我想我明白了
PDGFRA   p.D842V     457
PDGFRA   p.V561D     54
PDGFRA   p.S566_E571>R     50
PDGFRA   p.D842_H845del     35
PDGFRA   p.I843_D846delIMHD     24
PDGFRA   p.N659K     22
PDGFRA   p.I843_D846del     21
PDGFRA   p.D842Y     11
PDGFRA   p.I843_S847>T     9
PDGFRA   p.S566_E571>K     8
PDGFRA   p.M844_S847del     7
PDGFRA   p.N659Y     5
use strict;
use warnings;

my %matches = (
  PDGFRA => [
    [ "p.N659K",             22 ],
    [ "p.D842Y",             11 ],
    [ "p.I843_S847>T",        9 ],
    [ "p.D842_H845del",      35 ],
    [ "p.I843_D846delIMHD",  24 ],
    [ "p.I843_D846del",      21 ],
    [ "p.D842V",            457 ],
    [ "p.N659Y",              5 ],
    [ "p.M844_S847del",       7 ],
    [ "p.S566_E571>K",        8 ],
    [ "p.S566_E571>R",       50 ],
    [ "p.V561D",             54 ],
  ],
);

while (my ($key, $list) = each %matches) {

  my @sorted = sort { $list->[$b][1] <=> $list->[$a][1] } 0 .. $#$list;

  for my $item (@$list[@sorted]) {
    printf "%s   %s     %d\n", $key, @$item;
  }
}