2010-10-21 74 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

相關問題