2016-10-17 98 views
1

我寫了一個函數來從列表中刪除「重複項」。基於每個列表的子集從列表中刪除重複項

我的列表中的元素是:

[ip, email, phone number]. 

我想刪除得到了相同的電子郵件和電話號碼的子列表,我真的不關心IP地址。

,我目前使用的解決方案是:

def remove_duplicate_email_phone(data): 
    for i in range(len(data)): 
     for j in reversed(range(i+1,len(data))): 
      if data[i][1] == data[j][1] and data[i][2] == data[j][2] : 
       data.pop(j) 
    return data 

我想優化這個。花了超過30分鐘纔得到結果。

+1

使用'pop'名單上確實應該*絕不*可以任意做位置,在一個循環中。 –

回答

4

您的方法對列表中的每個元素進行全面掃描,使其花費O(N ** 2)(二次)時間。 list.pop(index)也是昂貴的,因爲index後面的所有內容都會上移,使您的解決方案接近O(N ** 3)立方時間。

使用一個集合並將(email, phonenumber)元組添加到它以檢查您是否已經看到該對;針對一組測試遏制花費O(1)固定的時間,這樣你就可以在O清理受騙者(N)總時間:

def remove_duplicate_email_phone(data): 
    seen = set() 
    cleaned = [] 
    for ip, email, phone in data: 
     if (email, phone) in seen: 
      continue 
     cleaned.append([ip, email, phone]) 
     seen.add((email, phone)) 
    return cleaned 

這將產生一個新的名單,舊列表保持不變。

+0

只是一個風格的問題 - 任何你使用'繼續'的原因,而不是僅僅處理未見的情況? – erip

+0

@erip:我喜歡避免過多的縮進級別。您確實可以反轉測試並縮小循環的其餘兩行。 –

0

另一種解決方案可能是使用groupby。

from itertools import groupby 
from operator import itemgetter 

deduped = [] 

data.sort(key=itemgetter(1,2)) 
for k, v in groupby(data, key=itemgetter(1,2): 
    deduped.append(list(v)[0]) 

或使用列表理解:

deduped = [next(v) for k, v in groupby(data, key=itemgetter(1,2))] 
+0

'groupby'要求對輸入進行排序。如果*未*已經按照分組標準排序,那麼當需要O(N)線性時間解決方案時,您必須以O(NlogN)成本添加,這是*非平凡的*代替。 –

+0

的確如此,但除非每天都需要對列表進行多次排序,並且它擁有數百萬條目,否則我認爲在分組之前進行排序所帶來的性能損失不是什麼大問題,甚至是顯而易見的。 –

+0

與不需要排序*的解決方案相比,它當然是顯而易見的。這是一個重要的警告;除非您需要將數據進行其他原因的排序並保持排序不會成爲負擔,否則肯定需要提及這個缺點。 –