perl哈希中的自定义字母排序

custom alphabetic sort in perl hash

我有一个散列,其结构如下:

my %my_hash=(
 'gee1' =>  {
                   'gene' => '20',
                   'mRNA' => '9',
                   'CDS' => '10',
                    'exon' => '10',
                    'product' => '10',
               },
'gee2'   =>  {
                     'gene' => 'aa',
                   'mRNA' => '9',
                   'CDS' => '1aa',
                    'exon' => '1aa',
                    'product' => 'ab',

               },
'gee4'   =>  {
                     'gene' => 'aa',
                   'rRNA' => '9',
                    'product' => 'ab',
                    'locus' => 'abc'

               },
'gee11'   =>  {
                     'gene' => 'aa',
                   'rRNA' => '9',
                    'product' => 'ab',
                    'locus' => 'abc'

               });

当我尝试按上述顺序打印散列时,使用以下代码:

for my $id ( sort { my ($anum) = ($a =~ /\w(\d+)$/);  my ($bnum) = ($b =~ /\w(\d+)$/);  $anum <=> $bnum} keys %my_hash)
{
    print "$id\n";
    for my $id1 (keys %{$my_hash{$id}})
    {
            print "\t$id1\n";
    }
}

输出是这样的:

gee1
    product
    exon
    gene
    mRNA
    CDS
gee2
    product
    exon
    CDS
    mRNA
    gene
gee4
    product
    locus
    gene
    rRNA
gee11
    rRNA
    gene
    product
    locus

你可以看到

product
exon
gene
mRNA
CDS

以上部分未按顺序排列
他们有什么方法可以对上面的部分进行排序吗?

订单如下: 基因、mRNA、rRNA、CDS、外显子、产物、基因座

您只需要将子键的显示顺序定义为:

sub by_int_suffix{
  my ($anum) = ($a =~ /\w(\d+)$/);
  my ($bnum) = ($b =~ /\w(\d+)$/);
  $anum <=> $bnum;
}

my @sub_key_order = qw(gene mRNA rRNA CDS exon product locus);
my @key_order = sort by_int_suffix keys %my_hash;
for my $id ( @key_order ){
    print "$id\n";
    for my $id1 (@sub_key_order){
            next unless exists $my_hash{$id}->{$id1};
            print "\t$id1\n";
    }
}

只需创建一个哈希,您可以根据该哈希对键进行排序:

my %keys_sort;
my $c = 0;
$keys_sort{$_} = $c++ for qw( gene mRNA rRNA CDS exon product locus );

并用它对它们进行排序:

for my $id1 (sort { $keys_sort{$a} <=> $keys_sort{$b} }
             keys %{ $my_hash{$id} }
) {

或者,直接使用列表,但 grep 只有相关键:

for my $id1 (grep exists $my_hash{$id}{$_},
             qw( gene mRNA rRNA CDS exon product locus )
) {

对于那种长度的排序例程,我强烈建议您停止尝试作为匿名子程序嵌入。

排序将引用子例程。这个子例程可以采用任何通用的 $a$b 和 return -1、0 或 1。(例如 cmp<=> 做)绝对基于任何你喜欢的标准。

这里需要特别注意的是 - 哈希值是明确无序的。事实上,它们通常是在幕后故意随机排序的。所以你需要 排序你的 'inner' 散列。

考虑到这一点,我会建议这样的事情:

use strict;
use warnings;

my %my_hash = (
    'gee1' => {
        'gene'    => '20',
        'mRNA'    => '9',
        'CDS'     => '10',
        'exon'    => '10',
        'product' => '10',
    },
    'gee2' => {
        'gene'    => 'aa',
        'mRNA'    => '9',
        'CDS'     => '1aa',
        'exon'    => '1aa',
        'product' => 'ab',

    },
    'gee4' => {
        'gene'    => 'aa',
        'rRNA'    => '9',
        'product' => 'ab',
        'locus'   => 'abc'

    },
    'gee11' => {
        'gene'    => 'aa',
        'rRNA'    => '9',
        'product' => 'ab',
        'locus'   => 'abc'

    }
);

sub sort_key_number {
    my ($anum) = ( $a =~ /(\d+)$/ );
    my ($bnum) = ( $b =~ /(\d+)$/ );

    #print "$anum, $bnum\n";
    return $anum <=> $bnum;
}

my @subkeys = qw ( gene mRNA rRNA CDS exon product locus );

foreach my $key ( sort {sort_key_number} keys %my_hash ) {
    print "\n", $key, ":\n";
    foreach my $subkey (@subkeys) {
        print "\t", $subkey, " = ", $my_hash{$key}{$subkey} || '', "\n";
    }
}

您已按 'key number' 对外部哈希进行排序。而你的内部散列,你每次都按特定顺序处理(由 @subkeys 定义)。

如果你想要一点技巧,最后一个 foreach 循环你可以用切片来做:

print join ( "\n\t", @{$my_hash{$key}}{@subkeys} );

但为了清楚起见,我决定不这样做。

只显示二级hash的keys有点不正常,不过答案是用一个单独的数组——这里我用了@required — 定义您想要的键和顺序

其他人已经解释了一些非常相似的东西,所以在这里我使用 grep 来稍微改变一下。我没有打印每个子哈希的键,而是选择打印存在对应键

@required 的元素

出于同样的原因,我使用 map 创建了一对需要在外部 sort 操作中进行比较的数值

use strict;
use warnings;
use 5.010;

my %my_hash=(
  gee1  => { CDS => 10, exon => 10, gene => 20, mRNA => 9, product => 10 },
  gee11 => { gene => "aa", locus => "abc", product => "ab", rRNA => 9 },
  gee2  => { CDS => "1aa", exon => "1aa", gene => "aa", mRNA => 9, product => "ab" },
  gee4  => { gene => "aa", locus => "abc", product => "ab", rRNA => 9 },
);

my @sorted_keys = sort {
  my ($aa, $bb) = map /(\d+)/, $a, $b;
  $aa <=> $bb;
} keys %my_hash;

my @required = qw/ gene mRNA rRNA CDS exon product locus /;

for my $key ( @sorted_keys ) {
  print "$key\n";
  for ( grep exists $my_hash{$key}{$_}, @required ) {
    print "    $_\n";
  }
}

输出

gee1
    gene
    mRNA
    CDS
    exon
    product
gee2
    gene
    mRNA
    CDS
    exon
    product
gee4
    gene
    rRNA
    product
    locus
gee11
    gene
    rRNA
    product
    locus

更新

那些丢失的哈希值困扰着我,我应该指出,您可以通过将最后一个 print 语句更改为

来非常简单地生成它们
print "    $_ = $my_hash{$key}{$_}\n"

现在的输出是

gee1
    gene = 20
    mRNA = 9
    CDS = 10
    exon = 10
    product = 10
gee2
    gene = aa
    mRNA = 9
    CDS = 1aa
    exon = 1aa
    product = ab
gee4
    gene = aa
    rRNA = 9
    product = ab
    locus = abc
gee11
    gene = aa
    rRNA = 9
    product = ab
    locus = abc

你一次做的太多了。使用两个循环。外循环用于对 %my_hash 基因进行排序,内循环用于对基因项目进行排序:

#! /usr/bin/env perl
#
use strict;
use warnings;
use feature qw(say);

my %my_hash=(
    'gee1' =>  { 'gene' => '20', 'mRNA' => '9', 'CDS' => '10', 'exon' => '10', 'product' => '10', },
    'gee2'   =>  { 'gene' => 'aa', 'mRNA' => '9', 'CDS' => '1aa', 'exon' => '1aa', 'product' => 'ab', },
    'gee4'   =>  { 'gene' => 'aa', 'rRNA' => '9', 'product' => 'ab', 'locus' => 'abc' },
    'gee11'   =>  { 'gene' => 'aa', 'rRNA' => '9', 'product' => 'ab', 'locus' => 'abc' },
);

for my $gene ( sort {fc $a cmp fc $b} keys %my_hash ) {
    say $gene;
    my %types = %{$my_hash{$gene}};
    for my $type ( sort {fc $a cmp fc $b} keys %types ) {
        say "    $type: " . $my_hash{$gene}->{$type};
    }
}

虽然我本可以直接对该数据集进行排序,但我注意到存在大写和小写键。使用纯 sort 命令将在 当前排序规则区域设置 中排序,这可能会将大写字母排在小写字母之前。使用 fc 折叠大小写。

也就是说,我是这样做的:

sort { fc $a cmp fc $b } @array;

而不只是:

sort @array;

您也可以考虑使用 Schwartzian Transformation,这可能会更快。如果不出意外,您可以使用 Schwartzian 转换按排序顺序保存数据,以防您不想立即打印出来。