我有相当大的哈希(大约10M键),我想从中删除一些元素.
我通常不喜欢使用delete或splice和我拉闸复制我想要什么,而不是删除的内容,我不知道.但是这一次,由于哈希是真正的大,我想我会直接将它删除.
所以我做的是这样的:
foreach my $key (keys %hash) {
if (should_be_deleted($key)) {
delete($hash{$key});
}
}
Run Code Online (Sandbox Code Playgroud)
它似乎工作正常.但是..如果我想在迭代它们之前删除一些元素怎么办?我将通过例子解释:
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...
}
}
Run Code Online (Sandbox Code Playgroud)
我想一些可能的解决方案 - 比如检查是否有键仍然存在,如在环或第一循环的第一步,创建密钥的列表中删除(并没有实际删除它们),则实际删除的另一个循环.
你对此有何看法?
UPDATE
似乎双通道方法已达成共识.但是,在第一遍中我仔细检查已经标记为删除的密钥,这是非常低效的.这有点递归,因为我不仅检查密钥,还计算应删除的其他密钥,尽管它们已经由原始密钥计算.
也许我需要使用一些更动态的数据结构来迭代密钥,这将动态更新?
我建议做两次通过,因为它更强大.散列顺序实际上是随机的,所以不能保证你会在相关的密钥之前看到"主要"密钥.例如,如果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
}
Run Code Online (Sandbox Code Playgroud)
根据问题中的示例,您可以使用 agrep来过滤出与您的匹配的键$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.
}
Run Code Online (Sandbox Code Playgroud)