Как отфильтровать список

В моей задаче у меня получился список с именами людей. Каждый элемент списка с именами показывает кто с кем общается. Для примера, список ниже:

friendships=[['Ann', 'Jhon'],['Fred', 'Hugh'],['Eric', 'James'],['James','Eric'],['Hugh','Fred'],['Jhon', 'Ann'],['Fred','Hugh','Gregory'],['Hugh', 'Fred','Gregory'],['Fred', 'Hugh', 'Gregory', 'Norman'],['Norman', 'Hugh', 'Fred', 'Gregory']]

На выходе:

[['Ann', 'Jhon'],
 ['Fred', 'Hugh'],
 ['Eric', 'James'],
 ['James', 'Eric'],
 ['Hugh', 'Fred'],
 ['Jhon', 'Ann'],
 ['Fred', 'Hugh', 'Gregory'],
 ['Hugh', 'Fred', 'Gregory'],
 ['Fred', 'Hugh', 'Gregory', 'Norman'],
 ['Norman', 'Hugh', 'Fred', 'Gregory']]

Мне надо его отфильтровать так, что те элементы, которые уже присутствуют в други (повторы), удалить их. На выходе мне надо такое:

[['Ann', 'Jhon'], ['James', 'Eric'], ['Fred', 'Hugh', 'Gregory', 'Norman']]

Подскажите, как это можно сделать?


Ответы (2 шт):

Автор решения: Vasyl Kolomiets

Этим свойством обладает set - множество не имеет повторений

unique = set(friendships)

Хотя в вашем случае ['Fred', 'Hugh'] равно ['Hugh', 'Fred'] как я понимаю, поэтому надо сперва преобразовать исходный список, упорядочивая данные например по алфавиту...
Можно было бы записать код так:

unique = set([set(el) for el in friendships ])

но он не будет работать, так как set можно брать только от хешируемых объектов, а set таким не является))

Решением является такая строка:

zz = set(tuple(sorted(el)) for el in friendships)

и результат:

zz
Out[11]: 
{('Ann', 'Jhon'),
 ('Eric', 'James'),
 ('Fred', 'Gregory', 'Hugh'),
 ('Fred', 'Gregory', 'Hugh', 'Norman'),
 ('Fred', 'Hugh')}

если же надо вернуться к списку списков а не кортежей, то тогда так:

lst = [list(z) for z in set(tuple(sorted(el)) for el in friendships)]

с результатом

[['Fred', 'Hugh'],
 ['Eric', 'James'],
 ['Fred', 'Gregory', 'Hugh'],
 ['Ann', 'Jhon'],
 ['Fred', 'Gregory', 'Hugh', 'Norman']]

Итого ваше решение в одну строку:

 lst = [list(z) for z in set(tuple(sorted(el)) for el in friendships)]
 print(lst)

дает

[['Fred', 'Hugh'], ['Eric', 'James'], ['Fred', 'Gregory', 'Hugh'], ['Ann', 'Jhon'], ['Fred', 'Gregory', 'Hugh', 'Norman']]

Добавлю, что это решение будет быстрее чем использование циклов.

С учетом уточнений задачи окончательное решение может выглядеть так:

zz = set(tuple(sorted(el)) for el in friendships)
zzz = zz.copy()    #  делаем копию множества для удалений, т.к. удалять из множества для цикла опасно
for el in zz:
    for elem in zz - {el}:     #    то есть перебираем множество без самого 'el'
        if set(el) <= set(elem):     #  если элементы set(el)  содержатся в set(elem)  элементах то удаляем
            zzz.remove(el)
            break
print(zzz)
{('Ann', 'Jhon'), ('Fred', 'Gregory', 'Hugh', 'Norman'), ('Eric', 'James')}

Post Scriptum: Как указал Stanislav Volodarskiy, в Python есть тип frozenset, который может быть элементами множества. Тогда вместо

zz = set(tuple(sorted(el)) for el in friendships)

можно написать

 zz = set(frozenset(el) for el in friendships)

Но тогда на выходе у вас получится сет фрозенсетов, которые возможно придется все равно превращать в списки или кортежи....

→ Ссылка
Автор решения: CrazyElf

Вот так:

s = set()
f = []
for l in sorted(friendships, key=len, reverse=True):
    if not set(l).issubset(s):
        s.update(l)
        f.append(l)
print(f)

Вывод:

[['Fred', 'Hugh', 'Gregory', 'Norman'], ['Ann', 'Jhon'], ['Eric', 'James']]
→ Ссылка