在迭代时我应该如何删除哈希元素?

时间:2010-10-21 15:34:18

标签: perl hash

我有相当大的哈希值(大约10M键),我想从中删除一些元素。

我通常不喜欢使用deletesplice,我最终会复制我想要的内容,而不是删除我不想要的内容。但这一次,由于哈希真的很大,我想我想直接删除它。

所以我正在做这样的事情:

foreach my $key (keys %hash) {
 if (should_be_deleted($key)) {
  delete($hash{$key});
 }
}

似乎工作正常。但是..如果我想在迭代它们之前删除一些元素怎么办?我将通过例子解释:

foreach my $key (keys %hash) {
 if (should_be_deleted($key)) {
  delete($hash{$key});
  # if $key should be deleted, so does "$key.a", "kkk.$key" and some other keys
  # I already know to calculate. I would like to delete them now...
 }
}

我想到了一些可能的解决方案 - 比如检查一个键是否仍然存在作为循环中的第一步或第一次循环并创建要删除的键列表(实际上没有删除它们),然后实际上在另一个循环中删除。 / p>

你对此有何看法?

更新

似乎双通道方法已达成共识。但是,在第一遍中我仔细检查已经标记为删除的密钥,这是非常低效的。这有点递归,因为我不仅检查密钥,还计算应删除的其他密钥,尽管它们已经由原始密钥计算。

也许我需要使用一些更动态的数据结构来迭代密钥,这将动态更新?

4 个答案:

答案 0 :(得分:8)

我建议两次通过,因为它更强大。散列顺序实际上是随机的,所以不能保证你会在相关的密钥之前看到“主”密钥。例如,如果should_be_deleted()仅检测到不需要的主键并计算了相关的主键,则最终可能会处理不需要的数据。两遍方法避免了这个问题。

my @unwanted;
foreach my $key (keys %hash) {
    if (should_be_deleted($key)) {
         push @unwanted, $key;
         # push any related keys onto @unwanted
    }
}

delete @hash{@unwanted};

foreach my $key (keys %hash) {
    # do something
}

答案 1 :(得分:4)

这个怎么样:

my %to_delete;

foreach my $key (keys %hash) {
    if (should_be_deleted($key)) {
        $to_delete{$key}++;
    }
    # add some other keys the same way...
}

delete @hash{keys %to_delete};

答案 2 :(得分:2)

根据问题中的示例,您可以使用grep过滤掉与$key令牌匹配的密钥。

<强>更新

您的评论已经澄清了您的需求。我的建议是确定符合您要求的索引,并相应地更新@keys。我们的想法是在循环时更新@keys,以避免不必要的迭代。

我在这里实现了简单的grep作为可自定义的函数。

sub matches { $_[0] =~ /$_[1]/ ? 1 : 0 }  # Simple grep implemented here

my @keys = keys %hash;  # @keys should initially contain all keys

while ( @keys ) {

    my $key = shift @keys;
    next unless should_be_deleted ($key);  # Skip keys that are wanted

    my @indexes_to_delete = grep { matches ($key, qr/$keys[$_]/) } 0 .. $#keys;

    delete @hash { @keys[@indexes_to_delete] };     # Remove the unwanted keys

    splice @keys, $_, 1 foreach @indexes_to_delete; # Removes deleted ...
                                                    # ... elements from @keys.
                                                    # Avoids needless iterations.
}

答案 3 :(得分:2)

您可以通过将其值设置为undef来标记要删除的哈希元素。这避免了在要删除的单独的键列表上浪费空间,以及避免对已标记为删除的元素进行检查。使用each代替for也会少浪费,while ( my ($key, $val) = each %hash ) { next unless defined $val and should_be_deleted($key); $hash{$key} = undef; $hash{$key.'a'} = undef; $hash{'kkk'.$key} = undef; } while ( my ($key, $val) = each %hash ) { delete $hash{$key} unless defined $val; } 在开始迭代循环之前构建所有散列键的列表

喜欢这个

code .