通过字母数字排序对哈希键进行排序

时间:2014-03-10 12:48:04

标签: perl sorting hash alphanumeric

我刚刚阅读了帖子 Sorting alphanumeric hash keys in Perl? 。但是我从Perl开始,我不太清楚它。

所以我有这样的哈希:

  %hash = (
        "chr1" => 1,
        "chr2" => 3,
        "chr19" => 14,
        "chr22" => 1,
        "X" => 2,
    )

我正在尝试获得这样的输出:

chr1
chr2
chr19
chr22

但我正在获得这样的输出:

chr1
chr19
chr2
chr22

我已编写此代码,但它正在创建上述错误输出:

foreach my $chr (sort {$a cmp $b} keys(%hash)) {
    my $total= $hash{$chr};
    my $differentpercent= ($differenthash{$chr} / $total)*100;
    my $round=(int($differentpercent*1000))/1000;
    print "$chr\t$hash{$chr}\t$differenthash{$chr}\t$round\n";
}

打印:

chr1    342421    7449    2.175
chr10    227648    5327    2.34
chr11    220415    4468    2.027
chr12    213263    4578    2.146
chr13    172379    3518    2.04
chr14    143534    2883    2.008
chr15    126441    2588    2.046
chr16    138239    3596    2.601
chr17    122137    3232    2.646
chr18    130275    3252    2.496
chr19    99876    2836    2.839
chr2    366815    8123    2.214

我该如何解决这个问题?

4 个答案:

答案 0 :(得分:4)

更新请注意@ Miller关于Sort::Naturally模块的一些缺点的评论。

你要求的是一个相对复杂的排序,它将每个字符串分成字母和数字字段,然后按字母顺序对字母和数字进行排序。

模块Sort::Naturally会按你的要求做,或者你可以写这样的东西。您似乎忽略了X键,因此我使用与案例无关的排序将其排序到最后。

use strict;
use warnings;

my %hash = map { $_ => 1 } qw(
    chr22  chr20  chr19  chr13  chr21  chr16  chr12  chr10  chr18
    chr17  chrY   chr5   chrX   chr8   chr14  chr6   chr3   chr9
    chr1   chrM   chr11  chr2   chr7   chr4   chr15
);

my @sorted_keys = sort {
    my @aa = $a =~ /^([A-Za-z]+)(\d*)/;
    my @bb = $b =~ /^([A-Za-z]+)(\d*)/;
    lc $aa[0] cmp lc $bb[0] or $aa[1] <=> $bb[1];
} keys %hash;

print "$_\n" for @sorted_keys;

<强>输出

chr1
chr2
chr3
chr4
chr5
chr6
chr7
chr8
chr9
chr10
chr11
chr12
chr13
chr14
chr15
chr16
chr17
chr18
chr19
chr20
chr21
chr22
chrM
chrX
chrY

使用Sort::Naturally模块(您可能需要安装它),您可以改为编写。

use strict;
use warnings;

use Sort::Naturally;

my %hash = map { $_ => 1 } qw(
    chr22  chr20  chr19  chr13  chr21  chr16  chr12  chr10  chr18
    chr17  chrY   chr5   chrX   chr8   chr14  chr6   chr3   chr9
    chr1   chrM   chr11  chr2   chr7   chr4   chr15
);

my @sorted_keys = nsort keys %hash;

print "$_\n" for @sorted_keys;

输出与上述相同。

答案 1 :(得分:2)

这也可以通过一个名为map-sort-map的常见Perl习语来解决:

#!/usr/bin/perl

use strict;
use warnings;

use Data::Dumper;

my %hash = (
   "chr1"  => 1,
   "chr2"  => 3,
   "chr19" => 14,
   "chr22" => 1,
);

my @sorted = map  { $_->[0]             }
             sort { $a->[1] <=> $b->[1] }
             map  { [$_, (/chr(\d+)/) || 0]  } keys %hash;

print Dumper \@sorted;
__END__
[
  'chr1',
  'chr2',
  'chr19',
  'chr22'
];

注意:与@Borodin不同,我选择排序X到前面,因为它没有指定,所以我只选择一个结束。

答案 2 :(得分:1)

这是我长期以来一直这样做的方式……我正在从 Borodin 的帖子中窃取代码以供参考。如果您了解正则表达式,Borodin 的排序代码非常容易理解。我更喜欢将复杂的排序放入 sub 中,否则它真的会变得混乱。无论如何,你去吧:

my %hash = (
    "chr1" => 1,
    "chr2" => 3,
    "chr19" => 14,
    "chr22" => 1,
    "X" => 2,
);

foreach my $key (sort {&sortalphanum} keys %hash)
{
  print "  $key = $hash{$key}\n";
}

sub sortalphanum
{
  my @aa = $a =~ /^([A-Za-z]+)(\d*)/;
  my @bb = $b =~ /^([A-Za-z]+)(\d*)/;
  lc $aa[0] cmp lc $bb[0] or $aa[1] <=> $bb[1];
}

答案 3 :(得分:0)

你可以试试这个:

#!/usr/bin/perl

use warnings;
use strict;

my %records;
while (<DATA>) {
    my ($key, undef) = split;
    $records{$key} = $_;
}

my @keys = sort {
    my ($aa) = $a =~ /(\d+)/;
    my ($bb) = $b =~ /(\d+)/;
    $aa <=> $bb;
} keys %records;

foreach my $key (@keys) {
    printf "$records{$key}";
}


__DATA__
chr1    342421  7449    2.175
chr10   227648  5327    2.34
chr11   220415  4468    2.027
chr12   213263  4578    2.146
chr13   172379  3518    2.04
chr14   143534  2883    2.008
chr15   126441  2588    2.046
chr16   138239  3596    2.601
chr17   122137  3232    2.646
chr18   130275  3252    2.496
chr19   99876   2836    2.839
chr2    366815  8123    2.214

输出:

$ perl t01.pl 
chr1    342421  7449    2.175
chr2    366815  8123    2.214
chr10   227648  5327    2.34
chr11   220415  4468    2.027
chr12   213263  4578    2.146
chr13   172379  3518    2.04
chr14   143534  2883    2.008
chr15   126441  2588    2.046
chr16   138239  3596    2.601
chr17   122137  3232    2.646
chr18   130275  3252    2.496
chr19   99876   2836    2.839