佩尔;如何按值过滤散列(指定条件)

Perl; how to filter an hash by value (specifying a condition)

我不是 perl 语言的专家,但我遇到了一个我无法解决的问题,即使在网上进行了长时间的研究。 简而言之,我有一个这样的散列:

my %HoH = (
    chr1 => { start => 30, end => 55, },
    chr1 => { start => 18, end => 21, },
    chr1 => { start => 30, end => 80, }
);

我只是想找到一种方法来过滤它(我的意思是,在输出中获取新的散列散列)以获取特定值。特别是,给定一个区间,比方说 40-60,我想要一个新的哈希散列,其中只有元素与这个区间重叠。

换句话说,我想得到输出:

my %HoH = (
    chr1 => { start => 30, end => 55, },
    chr1 => { start => 30, end => 80, }
);

作为第一次尝试,我想尝试这样的事情:

识别并删除所有带有 "end" < 40 的元素,并且: 识别并删除带有 "start" > 60.

的所有元素

所以我刚试过:

grep { $HoH{$_}{"end"} < 40 } keys(%HoH); 
delete $HoH{$_} for grep { $HoH{$_}{"end"} < 40} keys(%HoH);

但是在两个过滤器中的第一个之后,我发现输出中只有最后一个元素,我真的不明白错误在哪里:

hash size is 1
chr1: start=30 end=80 

打印出以下内容:

my $len = keys %HoH;
print "hash size is $len\n";

foreach my $chr ( keys %HoH ) {
   print "$chr: ";
   for my $position ( keys %{ $HoH{$chr} } ) {
      print "$position=$HoH{$chr}{$position} ";
   }
   print "\n";
}

这次对我来说似乎很复杂,如果你们中的任何人能帮助我,我会很高兴。

使用 Data::Dumper 检查你的散列,你会发现你没有你认为的数据结构:

use strict;
use warnings;
use Data::Dumper;

my %HoH = (
          chr1 => {
                   start => 30,
                   end => 55,
          },
          chr1 => {
                   start => 18,
                   end => 21,
                   },
          chr1 => {
                   start => 30,
                   end => 80,
                   },
            );
            
print Dumper \%HoH;     

$VAR1 = {
          'chr1' => {
                      'start' => 30,
                      'end' => 80
                    }
        };

正在发生的事情是它正在获取 chr1 的最后一个唯一条目。哈希键 必须 是唯一的

正如另一位发帖人所提到的 - 你的问题不是你的哈希合并,而是哈希不能有重复的键:

use strict;
use warnings;
use Data::Dumper;

my %HoH = (
    chr1 => { start => 30, end => 55, },
    chr2 => { start => 18, end => 21, },
    chr3 => { start => 30, end => 80, }
);


grep { $HoH{$_}{"end"} < 40 } keys(%HoH); 
delete $HoH{$_} for grep { $HoH{$_}{"end"} < 40} keys(%HoH);

print Dumper \%HoH;

这可以正常工作 - 请注意不同的哈希键。不过我会注意到 - 你正在迭代你的密钥,grepping它们,然后删除它们。可能更好:

foreach my $element ( keys %HoH ) {
    delete $HoH{$element}
        unless ( $HoH{$element}{start} < 40
              or $HoH{$element}{end}   > 60 );
}

print Dumper \%HoH;

你可以通过哈希数组来做你想做的事情:

use strict;
use warnings;
use Data::Dumper;

my @AoH = (
    { start => 30, end => 55, },
    { start => 18, end => 21, },
    { start => 30, end => 80, }
);

print Dumper \@AoH;

my @filtered = grep { $_->{start} > 40 or $_->{end} < 60 } @AoH;
print Dumper \@filtered;

注意 - 在您的原始示例中,您的 grep/delete 行执行相同的操作,您可以执行复合 grep 来测试这两种情况。