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

Просто вопрос стиля - любая причина, по которой вы используете 'continue' вместо того, чтобы просто обрабатывать невидимый случай? – 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 (NlogN) это * нетривиальный * с большим списком, когда имеется линейное решение времени O (N) вместо. –

+0

Это правда, но если список не нужно сортировать несколько раз в день, и у него есть миллионы записей, я не думаю, что штраф за производительность при сортировке перед группировкой - это большая сделка или даже заметная. –

+0

По сравнению с решением, которое не требует сортировки * вообще *, конечно, это будет заметно. Это важное предостережение; если вам не нужны данные для сортировки по другим причинам, и сохранение сортировки не будет бременем, это определенно нужно упомянуть как недостаток. –

Смежные вопросы