2014-12-09 2 views
1

У меня есть следующий список вложенных словарей:Как сделать уникальный список вложенных словарей в Python

[{'permission': 'full', 
    'permission_type': 'allow', 
    'trustee': {'id': 'SID:S-1-5-32-545', 
       'name': 'Users', 
       'type': 'group'}}, 
{'permission': 'full', 
    'permission_type': 'allow', 
    'trustee': {'id': 'SID:S-1-5-32-545', 
       'name': 'Users', 
       'type': 'group'}}, 
{'permission': 'full', 
    'permission_type': 'allow', 
    'trustee': {'id': 'SID:S-1-5-32-544', 
       'name': 'Administrators', 
       'type': 'group'}}] 

Я хочу, чтобы сделать его уникальным и пробовал разные предложения, но безуспешно. Может ли кто-нибудь помочь сделать его уникальным в python 2.6? В приведенных выше данных нет ключевого/уникального поля. Я ожидаю, что следующий результат (один член списка удаляется как полный дубликат):

[{'permission': 'full', 
    'permission_type': 'allow', 
    'trustee': {'id': 'SID:S-1-5-32-545', 
       'name': 'Users', 
       'type': 'group'}}, 
{'permission': 'full', 
    'permission_type': 'allow', 
    'trustee': {'id': 'SID:S-1-5-32-544', 
       'name': 'Administrators', 
       'type': 'group'}}] 
+2

вы можете определить «uniqify» точно? –

+0

Уникально какой мерой? Что все значения для заданных ключей одинаковы? Или это глуше, чем это? –

+1

удалите повторяющиеся элементы, в которых словари содержат одинаковые данные – Intra

ответ

5

Вы должны были бы отслеживать, если вы уже видели словарь уже. К сожалению, словари не хешируются и не отслеживают порядок, поэтому вам нужно конвертировать словари в нечто, что is hashable. frozenset() из пар ключ-значение (как кортежей) будет делать, но тогда вам нужно расплющить рекурсивно:

def set_from_dict(d): 
    return frozenset(
     (k, set_from_dict(v) if isinstance(v, dict) else v) 
     for k, v in d.iteritems()) 

Эти frozenset() объекты представляют словарные значения достаточно для отслеживания уникальных предметов:

seen = set() 
result = [] 
for d in inputlist: 
    representation = set_from_dict(d) 
    if representation in seen: 
     continue 
    result.append(d) 
    seen.add(representation) 

Это сохраняет исходный порядок вашего входного списка, минус дубликаты. Если вы используете Python 2.7 и выше, здесь было бы полезно использовать OrderedDict, но вы используете Python 2.6, поэтому нам нужно сделать это немного более подробно.

Приведенный выше подход использует O (N) время, один шаг для каждого словаря ввода, поскольку тестирование против множества принимает только O (1) постоянное время.

Демо:

>>> inputlist = [{'permission': 'full', 
... 'permission_type': 'allow', 
... 'trustee': {'id': 'SID:S-1-5-32-545', 
...    'name': 'Users', 
...    'type': 'group'}}, 
... {'permission': 'full', 
... 'permission_type': 'allow', 
... 'trustee': {'id': 'SID:S-1-5-32-545', 
...    'name': 'Users', 
...    'type': 'group'}}, 
... {'permission': 'full', 
... 'permission_type': 'allow', 
... 'trustee': {'id': 'SID:S-1-5-32-544', 
...    'name': 'Administrators', 
...    'type': 'group'}}] 
>>> def set_from_dict(d): 
...  return frozenset(
...   (k, set_from_dict(v) if isinstance(v, dict) else v) 
...   for k, v in d.iteritems()) 
... 
>>> seen = set() 
>>> result = [] 
>>> for d in inputlist: 
...  representation = set_from_dict(d) 
...  if representation in seen: 
...   continue 
...  result.append(d) 
...  seen.add(representation) 
... 
>>> from pprint import pprint 
>>> pprint(result) 
[{'permission': 'full', 
    'permission_type': 'allow', 
    'trustee': {'id': 'SID:S-1-5-32-545', 'name': 'Users', 'type': 'group'}}, 
{'permission': 'full', 
    'permission_type': 'allow', 
    'trustee': {'id': 'SID:S-1-5-32-544', 
       'name': 'Administrators', 
       'type': 'group'}}] 
+0

К сожалению, я не могу установить что-либо в системе и ограничиваться python 2.6 и не иметь «коллекций», я пытался запустить set_from_dict в моем списке и получить следующую ошибку: AttributeError: объект «list» не имеет атрибута «iteritems» ' – Intra

+0

@Intra: уже настроен. :-) –

+0

Спасибо, это выглядит как самый эффективный способ сделать это! – Intra

1

Ваши пункты dict так что вы не сможете использовать set напрямую (проверьте frozenset или this question/answer).
Но вы все еще можете сравнить элементы:

>>> l[0]==l[1] 
True 
>>> l[0]==l[2] 
False 

Так просто добавить свои элементы в новый список, если он еще не присутствует:

>>> l2=[] 
>>> for i in l: 
... if i not in l2: 
...  l2.append(i) 
... 
>>> pprint(l2) 
[{'permission': 'full', 
    'permission_type': 'allow', 
    'trustee': {'id': 'SID:S-1-5-32-545', 'name': 'Users', 'type': 'group'}}, 
{'permission': 'full', 
    'permission_type': 'allow', 
    'trustee': {'id': 'SID:S-1-5-32-544', 
       'name': 'Administrators', 
       'type': 'group'}}] 
+2

Это занимает квадратичное время, так как вы теперь проверяете * каждый новый словарь * на все словари, видимые до сих пор. –

+1

Что сказал Мартайн; это нормально для коротких списков, где у него меньше накладных расходов, чем метод Martijn, хотя он будет увязнуть для больших списков. Возможно, нам нужно вскрытие времени, чтобы увидеть, где точка безубыточности ... –

+0

@ PM2Ring: сравнение словаря выполняет ту же работу, что и мое преобразование в множества, при сравнении значений; ключи здесь одинаковы. Единственное различие заключается в том, что сравнение выполняется на C. Но этот метод делает гораздо больше сравнений; что точка безубыточности будет ниже, чем вы думаете. –