使用多个条件对嵌套哈希进行排序
Sort nested hash with multiple conditions
我是 perl 编程的新手,我有一个散列,可以这样表述:
$hash{"snake"}{ACB2} = [70, 120];
$hash{"snake"}{SGJK} = [183, 120];
$hash{"snake"}{KDMFS} = [1213, 120];
$hash{"snake"}{VCS2} = [21, 120];
...
$hash{"bear"}{ACB2} = [12, 87];
$hash{"bear"}{GASF} = [131, 87];
$hash{"bear"}{SDVS} = [53, 87];
...
$hash{"monkey"}{ACB2} = [70, 230];
$hash{"monkey"}{GMSD} = [234, 230];
$hash{"monkey"}{GJAS} = [521, 230];
$hash{"monkey"}{ASDA} = [134, 230];
$hash{"monkey"}{ASMD} = [700, 230];
哈希的结构总结如下:
%hash{Organism}{ProteinID}=(protein_length, total_of_proteins_in_that_organism)
我想根据某些条件对这个散列进行排序。首先,我只想考虑那些蛋白质总数大于100的生物,然后我想显示生物的名称以及最大的蛋白质和长度。
为此,我将采用以下方法:
foreach my $org (sort keys %hash) {
foreach my $prot (keys %{ $hash{$org} }) {
if ($hash{$org}{$prot}[1] > 100) {
@sortedarray = sort {$hash{$b}[0]<=>$hash{$a}[0]} keys %hash;
print $org."\n";
print @sortedarray[-1]."\n";
print $hash{$org}{$sortedarray[-1]}[0]."\n";
}
}
}
然而,这会打印生物体名称的次数与蛋白质总数一样多,例如,它会打印 "snake" 120 次。此外,这不是正确排序,因为我想我应该在排序行中使用变量 $org 和 $prot。
最后,输出应该是这样的:
snake
"Largest protein": KDMFS [1213]
monkey
"Largest protein": ASMD [700]
你在使用
use strict;
use warnings;
在脚本的开头?这样至少会突出一些问题的来源。没有它们,Perl 会默默地做一些它很容易指出愚蠢、毫无意义甚至最有可能是编程错误的事情。
作业
$hash{"snake"}{ACB2} = (70, 120);
只会分配值 120
,因为分配需要一个标量,但左边有一堆值。
要分配 arrayref,您必须明确声明它:
$hash{"snake"}{ACB2} = [70, 120];
你使用印记 ($
,@
, %
) 似乎不合适。
- 如果要处理标量或单个数组或散列值,请使用
$
。
- 如果要处理数组(或数组或散列切片(多个值)),请使用
@
;例如,@array[0,2]
会 return 数组中的第一个和第三个项目@array
).
- 如果要处理散列,请使用
%
。
所以
@sortedarray[-1]
应该是
$sortedarray[-1]
因为您只访问一个值。
如果我对您的理解正确,那么下面的代码应该可以满足您的期望。我将结果保存在哈希中,请随意以您想要的任何形式打印数据
use strict;
use warnings;
use Data::Dumper;
my $debug = 1;
my %data;
my $totalProteinsSearch = 100;
while( <DATA> ) {
chomp;
my @row = split ',';
$data{$row[0]}{$row[1]} = { proteinLength => $row[2], totalProteins => $row[3] };
}
print Dumper(\%data) if $debug == 1;
my %result;
while( my($organism,$value) = each %data ) {
while( my($proteinID, $data) = each %{$value} ) {
next if $data->{totalProteins} < $totalProteinsSearch;
$result{$organism} = {
proteinID => $proteinID,
proteinLength => $data->{proteinLength},
totalProteins => $data->{totalProteins}
}
if not defined $result{$organism}
or
$data->{proteinLength} > $result{$organism}{proteinLength};
}
}
print Dumper(\%result) if $debug;
__DATA__
snake,ACB2,70,120
snake,SGJK,183,120
snake,KDMFS,1213,120
snake,VCS2,21,120
bear,ACB2,12,87
bear,GASF,131,87
bear,SDVS,53,87
monkey,ACB2,70,230
monkey,GMSD,234,230
monkey,GJAS,521,230
monkey,ASDA,134,230
monkey,ASMD,700,230
您可以打印信息,例如如下所示[关闭调试$debug = 0
]
while( my($organism,$data) = each %result ) {
printf "%s\nLargetst protein: %s [%d]\n\n",
$organism,
$data->{proteinID},
$data->{proteinLength};
}
打印中所有数据排序
use warnings;
use strict;
use feature 'say';
use List::Util qw(max);
my %hash;
$hash{"snake"}{ACB2} = [70, 120];
$hash{"snake"}{SGJK} = [183, 120];
$hash{"snake"}{KDMFS} = [1213, 120];
$hash{"snake"}{VCS2} = [21, 120];
$hash{"bear"}{ACB2} = [12, 87];
$hash{"bear"}{GASF} = [131, 87];
$hash{"bear"}{SDVS} = [53, 87];
$hash{"monkey"}{ACB2} = [70, 230];
$hash{"monkey"}{GMSD} = [234, 230];
$hash{"monkey"}{GJAS} = [521, 230];
$hash{"monkey"}{ASDA} = [134, 230];
$hash{"monkey"}{ASMD} = [700, 230];
my @top_level_keys_sorted =
sort {
( max map { $hash{$b}{$_}->[0] } keys %{$hash{$b}} ) <=>
( max map { $hash{$a}{$_}->[0] } keys %{$hash{$a}} )
}
keys %hash;
for my $k (@top_level_keys_sorted) {
say $k;
say "\t$_ --> @{$hash{$k}{$_}}" for
sort { $hash{$k}{$b}->[0] <=> $hash{$k}{$a}->[0] }
keys %{$hash{$k}};
}
这首先根据要求按 arrayref 值中的第一个数字对顶级键进行排序。有了手头的排序键列表,我们然后进入每个键的 hashref 并进一步排序。该循环是我们根据需要调整以限制输出的内容(总数前 100,仅长度最大等)。
它打印
snake
KDMFS --> 1213 120
SGJK --> 183 120
ACB2 --> 70 120
VCS2 --> 21 120
monkey
ASMD --> 700 230
GJAS --> 521 230
GMSD --> 234 230
ASDA --> 134 230
ACB2 --> 70 230
bear
GASF --> 131 87
SDVS --> 53 87
ACB2 --> 12 87
我不知道输出应该显示所有“蛋白质总数高于 100 的生物体”(文本)还是只显示最大的一个(期望输出)所以我要离开这一切。根据需要切断。要仅获得最大的一个,请比较循环中每个键的最大值或参见 (同样的问题)。
请注意,哈希本身不能是 "sorted",因为它本质上是无序的。但是我们可以打印出排序的东西,如上所述,或者生成可以排序的辅助数据结构,如果需要的话。
您可以使用 List::Util reduce 获取每个有机体的最大值。
我知道您可能不熟悉 List::Util 中的这个函数,但对于这个问题的示例,它非常简单。
保存数据的结构的组成是数组散列,以生物体为键,整行存储在数组引用中作为散列值。
数据结构的选择部分取决于所需输出的形式。
#!/usr/bin/perl
use strict;
use warnings;
use List::Util qw/reduce/;
use constant {org_idx => 0, prot_idx => 1, len_idx => 2, cnt_idx => 3};
my %stuff;
while (<DATA>) {
chomp;
my @data = split /,/;
# saving all 4 items (per line)
# key is the organism
push @{$stuff{$data[org_idx]}}, \@data;
}
for my $org (sort keys %stuff) {
my $aref = $stuff{$org}; # an array of arrays reference
# to find the record with the max length
# $max becomes an array reference containing all 4 of the items as a list
my $max = reduce{$a->[len_idx] > $b->[len_idx] ? $a : $b} @$aref;
next if $max->[cnt_idx] < 100;
print $org, "\n";
print "Largest protein: $max->[prot_idx] [$max->[len_idx]]\n";
}
__DATA__
snake,ACB2,70,120
snake,SGJK,183,120
snake,KDMFS,1213,120
snake,VCS2,21,120
bear,ACB2,12,87
bear,GASF,131,87
bear,SDVS,53,87
monkey,ACB2,70,230
monkey,GMSD,234,230
monkey,GJAS,521,230
monkey,ASDA,134,230
monkey,ASMD,700,230
版画
monkey
Largest protein: ASMD [700]
snake
Largest protein: KDMFS [1213]
Data::%stuff
的转储器
$VAR1 = {
'monkey' => [
[
'monkey',
'ACB2',
70,
'230'
],
[
'monkey',
'GMSD',
234,
'230'
],
[
'monkey',
'GJAS',
521,
'230'
],
[
'monkey',
'ASDA',
134,
'230'
],
[
'monkey',
'ASMD',
700,
'230'
]
],
'bear' => [
[
'bear',
'ACB2',
12,
'87'
],
[
'bear',
'GASF',
131,
'87'
],
[
'bear',
'SDVS',
53,
'87'
]
],
'snake' => [
[
'snake',
'ACB2',
70,
'120'
],
[
'snake',
'SGJK',
183,
'120'
],
[
'snake',
'KDMFS',
1213,
'120'
],
[
'snake',
'VCS2',
21,
'120'
]
]
};
我是 perl 编程的新手,我有一个散列,可以这样表述:
$hash{"snake"}{ACB2} = [70, 120];
$hash{"snake"}{SGJK} = [183, 120];
$hash{"snake"}{KDMFS} = [1213, 120];
$hash{"snake"}{VCS2} = [21, 120];
...
$hash{"bear"}{ACB2} = [12, 87];
$hash{"bear"}{GASF} = [131, 87];
$hash{"bear"}{SDVS} = [53, 87];
...
$hash{"monkey"}{ACB2} = [70, 230];
$hash{"monkey"}{GMSD} = [234, 230];
$hash{"monkey"}{GJAS} = [521, 230];
$hash{"monkey"}{ASDA} = [134, 230];
$hash{"monkey"}{ASMD} = [700, 230];
哈希的结构总结如下:
%hash{Organism}{ProteinID}=(protein_length, total_of_proteins_in_that_organism)
我想根据某些条件对这个散列进行排序。首先,我只想考虑那些蛋白质总数大于100的生物,然后我想显示生物的名称以及最大的蛋白质和长度。
为此,我将采用以下方法:
foreach my $org (sort keys %hash) {
foreach my $prot (keys %{ $hash{$org} }) {
if ($hash{$org}{$prot}[1] > 100) {
@sortedarray = sort {$hash{$b}[0]<=>$hash{$a}[0]} keys %hash;
print $org."\n";
print @sortedarray[-1]."\n";
print $hash{$org}{$sortedarray[-1]}[0]."\n";
}
}
}
然而,这会打印生物体名称的次数与蛋白质总数一样多,例如,它会打印 "snake" 120 次。此外,这不是正确排序,因为我想我应该在排序行中使用变量 $org 和 $prot。
最后,输出应该是这样的:
snake
"Largest protein": KDMFS [1213]
monkey
"Largest protein": ASMD [700]
你在使用
use strict;
use warnings;
在脚本的开头?这样至少会突出一些问题的来源。没有它们,Perl 会默默地做一些它很容易指出愚蠢、毫无意义甚至最有可能是编程错误的事情。
作业
$hash{"snake"}{ACB2} = (70, 120);
只会分配值 120
,因为分配需要一个标量,但左边有一堆值。
要分配 arrayref,您必须明确声明它:
$hash{"snake"}{ACB2} = [70, 120];
你使用印记 ($
,@
, %
) 似乎不合适。
- 如果要处理标量或单个数组或散列值,请使用
$
。 - 如果要处理数组(或数组或散列切片(多个值)),请使用
@
;例如,@array[0,2]
会 return 数组中的第一个和第三个项目@array
). - 如果要处理散列,请使用
%
。
所以
@sortedarray[-1]
应该是
$sortedarray[-1]
因为您只访问一个值。
如果我对您的理解正确,那么下面的代码应该可以满足您的期望。我将结果保存在哈希中,请随意以您想要的任何形式打印数据
use strict;
use warnings;
use Data::Dumper;
my $debug = 1;
my %data;
my $totalProteinsSearch = 100;
while( <DATA> ) {
chomp;
my @row = split ',';
$data{$row[0]}{$row[1]} = { proteinLength => $row[2], totalProteins => $row[3] };
}
print Dumper(\%data) if $debug == 1;
my %result;
while( my($organism,$value) = each %data ) {
while( my($proteinID, $data) = each %{$value} ) {
next if $data->{totalProteins} < $totalProteinsSearch;
$result{$organism} = {
proteinID => $proteinID,
proteinLength => $data->{proteinLength},
totalProteins => $data->{totalProteins}
}
if not defined $result{$organism}
or
$data->{proteinLength} > $result{$organism}{proteinLength};
}
}
print Dumper(\%result) if $debug;
__DATA__
snake,ACB2,70,120
snake,SGJK,183,120
snake,KDMFS,1213,120
snake,VCS2,21,120
bear,ACB2,12,87
bear,GASF,131,87
bear,SDVS,53,87
monkey,ACB2,70,230
monkey,GMSD,234,230
monkey,GJAS,521,230
monkey,ASDA,134,230
monkey,ASMD,700,230
您可以打印信息,例如如下所示[关闭调试$debug = 0
]
while( my($organism,$data) = each %result ) {
printf "%s\nLargetst protein: %s [%d]\n\n",
$organism,
$data->{proteinID},
$data->{proteinLength};
}
打印中所有数据排序
use warnings;
use strict;
use feature 'say';
use List::Util qw(max);
my %hash;
$hash{"snake"}{ACB2} = [70, 120];
$hash{"snake"}{SGJK} = [183, 120];
$hash{"snake"}{KDMFS} = [1213, 120];
$hash{"snake"}{VCS2} = [21, 120];
$hash{"bear"}{ACB2} = [12, 87];
$hash{"bear"}{GASF} = [131, 87];
$hash{"bear"}{SDVS} = [53, 87];
$hash{"monkey"}{ACB2} = [70, 230];
$hash{"monkey"}{GMSD} = [234, 230];
$hash{"monkey"}{GJAS} = [521, 230];
$hash{"monkey"}{ASDA} = [134, 230];
$hash{"monkey"}{ASMD} = [700, 230];
my @top_level_keys_sorted =
sort {
( max map { $hash{$b}{$_}->[0] } keys %{$hash{$b}} ) <=>
( max map { $hash{$a}{$_}->[0] } keys %{$hash{$a}} )
}
keys %hash;
for my $k (@top_level_keys_sorted) {
say $k;
say "\t$_ --> @{$hash{$k}{$_}}" for
sort { $hash{$k}{$b}->[0] <=> $hash{$k}{$a}->[0] }
keys %{$hash{$k}};
}
这首先根据要求按 arrayref 值中的第一个数字对顶级键进行排序。有了手头的排序键列表,我们然后进入每个键的 hashref 并进一步排序。该循环是我们根据需要调整以限制输出的内容(总数前 100,仅长度最大等)。
它打印
snake KDMFS --> 1213 120 SGJK --> 183 120 ACB2 --> 70 120 VCS2 --> 21 120 monkey ASMD --> 700 230 GJAS --> 521 230 GMSD --> 234 230 ASDA --> 134 230 ACB2 --> 70 230 bear GASF --> 131 87 SDVS --> 53 87 ACB2 --> 12 87
我不知道输出应该显示所有“蛋白质总数高于 100 的生物体”(文本)还是只显示最大的一个(期望输出)所以我要离开这一切。根据需要切断。要仅获得最大的一个,请比较循环中每个键的最大值或参见
请注意,哈希本身不能是 "sorted",因为它本质上是无序的。但是我们可以打印出排序的东西,如上所述,或者生成可以排序的辅助数据结构,如果需要的话。
您可以使用 List::Util reduce 获取每个有机体的最大值。
我知道您可能不熟悉 List::Util 中的这个函数,但对于这个问题的示例,它非常简单。
保存数据的结构的组成是数组散列,以生物体为键,整行存储在数组引用中作为散列值。
数据结构的选择部分取决于所需输出的形式。
#!/usr/bin/perl
use strict;
use warnings;
use List::Util qw/reduce/;
use constant {org_idx => 0, prot_idx => 1, len_idx => 2, cnt_idx => 3};
my %stuff;
while (<DATA>) {
chomp;
my @data = split /,/;
# saving all 4 items (per line)
# key is the organism
push @{$stuff{$data[org_idx]}}, \@data;
}
for my $org (sort keys %stuff) {
my $aref = $stuff{$org}; # an array of arrays reference
# to find the record with the max length
# $max becomes an array reference containing all 4 of the items as a list
my $max = reduce{$a->[len_idx] > $b->[len_idx] ? $a : $b} @$aref;
next if $max->[cnt_idx] < 100;
print $org, "\n";
print "Largest protein: $max->[prot_idx] [$max->[len_idx]]\n";
}
__DATA__
snake,ACB2,70,120
snake,SGJK,183,120
snake,KDMFS,1213,120
snake,VCS2,21,120
bear,ACB2,12,87
bear,GASF,131,87
bear,SDVS,53,87
monkey,ACB2,70,230
monkey,GMSD,234,230
monkey,GJAS,521,230
monkey,ASDA,134,230
monkey,ASMD,700,230
版画
monkey
Largest protein: ASMD [700]
snake
Largest protein: KDMFS [1213]
Data::%stuff
$VAR1 = {
'monkey' => [
[
'monkey',
'ACB2',
70,
'230'
],
[
'monkey',
'GMSD',
234,
'230'
],
[
'monkey',
'GJAS',
521,
'230'
],
[
'monkey',
'ASDA',
134,
'230'
],
[
'monkey',
'ASMD',
700,
'230'
]
],
'bear' => [
[
'bear',
'ACB2',
12,
'87'
],
[
'bear',
'GASF',
131,
'87'
],
[
'bear',
'SDVS',
53,
'87'
]
],
'snake' => [
[
'snake',
'ACB2',
70,
'120'
],
[
'snake',
'SGJK',
183,
'120'
],
[
'snake',
'KDMFS',
1213,
'120'
],
[
'snake',
'VCS2',
21,
'120'
]
]
};