我刚刚阅读了帖子 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
我该如何解决这个问题?
答案 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