0

我有两个哈希数组。我想根据第一个变量缩小第二个。

第一个数组包含键seqname为 , source, feature, start, end, score, strand, frame,geneID和的哈希值transcriptID

第二个数组包含键 organism为 、geneIDnumbermotifnumberpositionstrand的散列sequence

我想要做的是从第一个哈希数组中删除所有具有变量的哈希,该变量在第二个数组的任何哈希中geneID找不到。- 注意两种类型的哈希都有geneID键。简单地说,我想将这些散列保留在第一个数组中,这些散列的geneID值可以在第二个数组的散列中找到。

到目前为止,我的尝试是使用两个循环:

my @subset # define a new array for the wanted hashes to go into.

for my $i (0 .. $#first_hash_array){  # Begin loop to go through the hashes of the first array.

    for my $j (0 .. $#second_hash_array){ # Begin loop through the hashes of the 2nd array.

        if ($second_hash_array[$j]{geneID} =~ m/$first_hash_array[$i]{geneID}/)
        {
           push @subset, $second_hash_array[$j];
        }

    }

}

但是,我不确定这是解决此问题的正确方法。

4

2 回答 2

2

首先,$a =~ /$b/不检查是否相等。你需要

$second_hash_array[$j]{geneID} =~ m/^\Q$first_hash_array[$i]{geneID}\E\z/

或者干脆

$second_hash_array[$j]{geneID} eq $first_hash_array[$i]{geneID}

为了那个原因。


第二,

for my $i (0 .. $#first_hash_array) {
   ... $first_hash_array[$i] ...
}

可以更简洁地写成

for my $first (@first_hash_array) {
   ... $first ...
}

名单上的下一个是

for my $second (@second_hash_array) {
    if (...) {
       push @subset, $second;
    }
}

可以$second多次添加@subset。您要么需要添加一个last

# Perform the push if the condition is true for any element.
for my $second (@second_hash_array) {
   if (...) {
      push @subset, $second;
      last;
   }
}

或移出push循环

# Perform the push if the condition is true for all elements.
my $flag = 1;
for my $second (@second_hash_array) {
   if (!...) {
      $flag = 0;
      last;
   }
}

if ($flag) {
   push @subset, $second;
}

取决于你想做什么。


要从数组中删除,可以使用splice. 但是从数组中删除会弄乱所有索引,因此最好向后迭代数组(从最后一个索引到第一个索引)。

它不仅复杂,而且价格昂贵。每次拼接时,数组中的所有后续元素都需要移动。

更好的方法是过滤元素并将结果元素分配给数组。

my @new_first_hash_array;
for my $first (@first_hash_array) {
   my $found = 0;
   for my $second (@second_hash_array) {
      if ($first->{geneID} eq $second->{geneID}) {
         $found = 1;
         last;
      }
   }

   if ($found) {
      push @new_first_hash_array, $first;
   }
}

@first_hash_array = @new_first_hash_array;

反复迭代@second_hash_array是不必要的昂贵。

my %geneIDs_to_keep;
for (@second_hash_array) {
   ++$geneIDs_to_keep{ $_->{geneID} };
}

my @new_first_hash_array;
for (@first_hash_array) {
   if ($geneIDs_to_keep{ $_->{geneID} }) {
      push @new_first_hash_array, $_;
   }
}

@first_hash_array = @new_first_hash_array;

最后,我们可以将其替换for为 agrep以给出以下简单有效的答案:

my %geneIDs_to_keep;
++$geneIDs_to_keep{ $_->{geneID} } for @second_hash_array;

@first_hash_array = grep $geneIDs_to_keep{ $_->{geneID} }, @first_hash_array;
于 2013-04-15T18:00:39.147 回答
1

我就是这样做的。

为所需的geneIDs创建一个数组req_geneID,并将第二个哈希的所有geneIds 放入其中。

遍历第一个哈希并检查geneId是否包含在req_geneID数组中。(在 ruby​​ 中使用“include”很容易,但你可以perl 中尝试)

和,

最后在 perl中使用this删除与req_geneID中的任何geneID 不匹配的哈希

for (keys %hash)
{
    delete $hash{$_};
}

希望这可以帮助.. :)

于 2013-04-15T18:00:59.063 回答