2016-10-17 61 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

与不需要排序*的解决方案相比,它当然是显而易见的。这是一个重要的警告;除非您需要将数据进行其他原因的排序并保持排序不会成为负担,否则肯定需要提及这个缺点。 –