2010-10-21 66 views
12

我有相当大的散列(一些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... 
} 
} 

我想一些可能的解决方案 - 比如检查是否有键仍然存在,如在环或第一循环的第一步,创建密钥的列表中删除(并没有实际删除他们),然后在另一个循环中实际删除。

您对此有何看法?

UPDATE

这似乎是双通的办法有一个共识。然而,从第一遍的过程中我仔细检查已经标记为删除的键是非常低效的。这是有点递归的,因为不仅我检查了密钥,还计算了应该删除的其他密钥,尽管它们已经由原始密钥计算出来了。

也许我需要使用一些更加动态的数据结构遍历键,将动态更新?

+0

***“我仔细检查按键之前所有的哈希键的列表那已经标记为删除“***看到我的解决方案节俭的替代 – Borodin 2015-07-11 15:20:04

回答

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. 
} 
+0

我的例子是简单的,但这不是问题 - 我知道如何找到需要删除的键,无论是使用grep或任何魔术函数获取应该删除的密钥并返回应该同时删除的其他密钥的列表。问题是如何很好地克服这样一个事实,即如果我在循环到达之前删除一个键,我仍然会在稍后到达它,尽管它现在还不存在。我想一个简单的'next除非存在($ hash {$ key})'会做,但我想知道是否还有其他建议。 – 2010-10-21 15:55:58

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}; 
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 
} 
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; 
} 
+0

假设'undef'不是有效值的好方法。在完整散列上进行第二遍传递时,存在时间/内存折衷,而不是将其限制为应该删除的键。您可以通过立即删除主键(可以安全删除'each'返回的最新元素)以便第二遍更短。 – 2015-07-11 17:40:50