3

我有一个看起来像这样的哈希:

my $hash = {
    level1_f1 => {
                  level2_f1 => 'something',
                  level2_f2 => 'another thing'
    },
    level1_f2 => {
                  level2_f3 => 'yet another thing',
                  level2_f4 => 'bla bla'
                  level2_f5 => ''
    }
...
 }

我还得到了一个与“level2”键对应的值列表,我想知道它是否存在于哈希中。

@list = ("level2_f2", "level2_f4", "level2_f99")

我不知道@list 的每个元素属于哪个“level1”键。我认为找到它们是否存在的唯一方法是使用一个 foreach 循环来遍历@list,另一个 foreach 循环来遍历 %hash 的键并检查

foreach my $i (@array) {
  foreach my $k (keys %hash) {
     if (exists $hash{$k}{$list[$i]})
 }
}

但我想知道是否有更有效或更优雅的方式来做到这一点。我找到的所有答案都要求您知道“level1”键,而我不知道。

谢谢!!

4

3 回答 3

5

使用价值观

for my $inner_hash (values %$hash) {
    say grep exists $inner_hash->{$_}, @list;
}
于 2019-12-04T16:50:18.537 回答
1

您不需要遍历“整个哈希”。

您必须遍历外部哈希的元素,因为您想检查每个元素的值,但您不需要遍历内部哈希的元素。您的解决方案已经证明了这一点。

因此,您的解决方案尽可能高效,至少就其可扩展性而言。您只能执行小的优化,例如一旦找到匹配项就停止。

for my $i (@list) {
   while ( my (undef, $inner) = each(%hash) ) {
      if (exists($inner->{$i}) {
         ...
         last;
      }
   }

   keys(%hash);   # Reset iterator since it might not be exhausted.
}

作为一种微优化,反转循环的嵌套可能是有益的。

my %list = map { $_ => 1 } @list;

while ( my (undef, $inner) = each(%hash) ) {
   while (defined( my $k = each(%$inner) )) {
      if ($list{$k}) {
         delete($list{$k});
         ...
         last if !keys(%list);
      }

   }

   keys(%$inner);   # Reset iterator since it might not be exhausted.
   last if !keys(%list);
}

keys(%hash);   # Reset iterator since it might not be exhausted.

如果哈希值很小,这些更改实际上可能会减慢速度。

老实说,如果确实存在速度问题,那么问题在于您为要在其上运行的查询类型使用了错误的数据结构!

于 2019-12-05T07:55:38.953 回答
1

您必须循环所有 level1 键。但是,如果您不需要知道哪些键匹配并且只关心任何键的存在,那么您不必明确询问列表中的每个成员。你可以说

foreach my $k (keys %hash) {
   if ( @{ $hash{$k} }{ @list } )
   {
   }
}

哈希切片将返回子哈希中所有在列表中具有匹配键的值。列表中不在子哈希中的键将被忽略。

但是请注意,这可能比您真正需要的工作更多。

于 2019-12-04T17:21:55.613 回答