Уникальный список диктов на основе ключей - PullRequest
4 голосов
/ 06 декабря 2010

У меня есть список dics:

     data = {}
     data['key'] = pointer_key
     data['timestamp'] = timestamp
     data['action'] = action
     data['type'] = type
     data['id'] = id

     list = [data1, data2, data3, ... ]

Как я могу гарантировать, что для каждого элемента данных в списке только один такой элемент существует для каждого "ключа"?Если есть две клавиши, как показано ниже, победит самая последняя отметка времени:

    list = [{'key':1,'timestamp':1234567890,'action':'like','type':'photo',id:245},
            {'key':2,'timestamp':2345678901,'action':'like','type':'photo',id:252},
            {'key':1,'timestamp':3456789012,'action':'like','type':'photo',id:212}]

    unique(list)

    list = [{'key':2,'timestamp':2345678901,'action':'like','type':'photo',id:252},
            {'key':1,'timestamp':3456789012,'action':'like','type':'photo',id:212}]

Спасибо.

Ответы [ 10 ]

4 голосов
/ 08 декабря 2012

Вот мое решение:

def uniq(list_dicts):
    return [dict(p) for p in set(tuple(i.items()) 
        for i in list_dicts)]

надеюсь, это кому-нибудь поможет.

3 голосов
/ 09 ноября 2013

Мне это нужно, но мне не понравились ответы здесь. Поэтому я сделал эту простую и производительную версию.

def list_of_seq_unique_by_key(seq, key):
    seen = set()
    seen_add = seen.add
    return [x for x in seq if x[key] not in seen and not seen_add(x[key])]

# Usage
# If you want most recent timestamp to win, just sort by timestamp first
list = sorted(list, key=lambda k: k['timestamp'], reverse=True)
# Remove everything with a duplicate value for key 'key'
list = list_of_seq_unique_by_key(list, 'key')
1 голос
/ 07 декабря 2010

Вы также можете использовать словарь списков, где каждая позиция списка представляет определенное значение.

<code>data = {}
data[pointer_key] = [timestamp, action, type, id]
if new_pointer_key in data:
    if this_timestamp > data[new_pointer_key][0]:   ## first element of list=timestamp
        data[new_pointer_key] = [new_timestamp,  new_action, new_type, new_id] 
1 голос
/ 06 декабря 2010

Я думаю, что вы имеете в виду, что каждое поле 'key' должно быть уникальным для всех данных.

Хорошо, давайте начнем с того, что вы, вероятно, должны сделать: используйте базу данных, они любят решать эту проблему.

Вы также можете выполнять работу вручную, например:

def unique_keys( items):
    seen = set()
    for item in items:
        key = item['key']
        if key not in seen:
             seen.add(key)
             yield item
        else:
             # its a duplicate key, do what?
             pass # drops it

print list(unique_keys(data_list))

Или, может быть, вам нужна структура данных, которая хранит существующие ключи и не позволяет создавать новые данные для ключей, которые уже существуют ...?

1 голос
/ 06 декабря 2010

Чтобы уточнить, у вас есть несколько словарей, но вы хотите уникальные данные ['ключ']?Например, если data1 ['key'] = 'hello', вы хотите убедиться, что data2 ['key'] = 'hello' не разрешено?Вы хотите просто вызвать ошибку?Это способ проверить, что это хорошо.(Также не рекомендуется называть ваш список «список», так как список является типом данных в Python)

datalist = [datadict1, datadict2, datadict3]
big_key_list = []
for datadict in datalist:
    curkey = datadict.get('key')
    if curkey not in big_key_list:
        big_key_list.append(curkey)
    else:
        raise Exception("Key %s in two data dicts" % curkey)

Теперь лучшим способом сделать это будет создание нового класса, наследуемого от dict, который содержит поддиктарии,но не позволяет нескольким ключам иметь одинаковое значение.Таким образом, ошибки возникают при назначении, а не вы можете просто проверить, все ли в порядке (и не знаете, что делать, если что-то не так, кроме как выдавать ошибку).что вы, вероятно, хотите сделать, у вас неправильно настроены данные.Я говорю это так, как будто вы хотите иметь отдельный словарь для каждой записи.Это почти наверняка не элегантный способ сделать это.

Сначала создайте класс:

class MyDataObject(object):
    def __init__(self, **kwargs):
        for k,v in kwargs:
            self.__dict__[k] = v

или, если они всегда будут иметь все 4 фиксированных параметра:

class MyDataObject(object):
    def __init__(self, timestamp, action, obj_type, obj_id):
        self.timestamp = timestamp
        self.action = action
        self.type = obj_type
        self.id = obj_id

Затем просто определите свои типы данных.

data = {}
data['key1'] = MyDataObject(timestamp='some timestamp', action='some action', type='some type', id = 1234)
data['key2'] = MyDataObject(timestamp='some timestamp2', action='some action2', type='some type2', id = 1235)

Вы бы получили доступ к своим данным, как:

data['key1'].timestamp # returns 'some timestamp'
data['key2'].action # returns 'some action2'

, или вы можете даже получить доступ, используя dict () (например, это полезно, если у вас есть переменная x = 'action ', и вы хотите получить к нему доступ.)

data['key1'].__dict__('action') # returns 'some action'
data['key2'].__dict__('timestamp') # returns 'some timestamp2'

Теперь у вас есть просто словарь объектов, в котором ключ уникален, а данные, связанные с ним, хранятся как один объект (типа MyDataObject)..

0 голосов
/ 09 ноября 2016
>>> def unique(l):
...     return {k['key']:k for k in l}.values()
...
>>> print(unique([ {'key':1,'timestamp':1234567890,'action':'like','type':'photo',id:245},
...                {'key':2,'timestamp':2345678901,'action':'like','type':'photo',id:252},
...                {'key':1,'timestamp':3456789012,'action':'like','type':'photo',id:212} ]))
dict_values([{<built-in function id>: 212, 'type': 'photo', 'key': 1, 'timestamp': 3456789012, 'action': 'like'}, {<built-in function id>: 252, 'type': 'photo', 'key': 2, 'timestamp': 2345678901, 'action': 'like'}])
0 голосов
/ 15 октября 2015

Функция groupby из itertools может быть полезна здесь:

def unique(items, key, order=None):
    sort_func = (lambda v: (key(v), order(v))) if order else key
    groups = itertools.groupby(sorted(items, key=sort_func), key)
    return [group.next() for unused_key, group in groups]

или

def unique(items, key, order=None):
    groups = itertools.groupby(sorted(items, key=key), key)
    return [max(group, key=order) for unused_key, group in groups]

Группирует элементы, которые выглядят одинаково, на основе дополнительного ключа. Использование его для данных, отсортированных по одному и тому же классификатору, объединит их в группы Взятие первого элемента сделает их уникальными. Чтобы разрешить опцию «отсортировано по отметке времени», мы можем сортировать по ключу, а также по отметке времени, а затем группировать только по ключу. Тогда вы можете использовать его, как показано ниже:

data = [{'key':1, 'timestamp':1234567890, 'action':'like', 'type':'photo', 'id':245},
        {'key':2, 'timestamp':2345678901, 'action':'like', 'type':'photo', 'id':252},
        {'key':1, 'timestamp':3456789012, 'action':'like', 'type':'photo', 'id':212}]

# unique(data)
key = lambda d: d['key']  # Group by key
order = lambda d: -d['timestamp']  # Sort by descending order timestamp
data = unique(data, key, order_func=order)

data == [{'key':1, 'timestamp':3456789012, 'action':'like', 'type':'photo', 'id':212},
         {'key':2, 'timestamp':2345678901, 'action':'like', 'type':'photo', 'id':252}]

Мы заставляем ключ быть первым в функции сортировки, чтобы обеспечить правильную группировку независимо от порядка.

Это решение изменяет порядок ваших предметов, хотя оно обладает преимуществом безобидного хранения и временной сложности.

0 голосов
/ 06 декабря 2010

Когда вы делаете такие вещи, обычно это хороший признак того, что где-то есть ошибка в дизайне.
Но это можно сделать:

from operator import itemgetter

def unique(list_of_dicts):
    _sorted = sorted(list_of_dicts, key=itemgetter('timestamp'), reverse=True)
    known_keys = set()
    result = []
    for d in _sorted:
        key = d['key']
        if key in known_keys: continue
        known_keys.add(key)
        result.append(d)
    return result

Вывод ( примечание: оно меняетсяordering ):

[{'action': 'like', 'timestamp': 3456789012, 'type': 'photo', 'id': 212, 'key': 1},
{'action': 'like', 'timestamp': 2345678901, 'type': 'photo', 'id': 252, 'key': 2}]

И теперь, когда ключи уникальны (с последними временными метками, которые по желанию сохраняются), неплохо было бы преобразовать их во что-то, что лучше отражает ваши данные, , как предложеноот jimbob :

class MyDataObject(object):
    def __init__(self, timestamp, action, obj_type, obj_id):
        self.timestamp = timestamp
        self.action = action
        self.type = obj_type
        self.id = obj_id

data = {}
for action in unique(_list):
    key = action['key']
    data[key] = MyDataObject(action['timestamp'], action['action'],
        action['type'], action['id'])
0 голосов
/ 06 декабря 2010
>>> d = {'a': 1, 'b': 2, 'a': 3}
>>> d
{'a': 3, 'b': 2}

Так что в диктовке есть уникальность ключа.

Обновление: (на основании вашего комментария)

Если вы ищете один ключ, несколько значений, вы подкласс dict, как:

>>> class custom_dict(dict):
      def __setitem__(self, key, value):
        self.setdefault(key, []).append(value)

>>> m = custom_dict()
>>> m['key'] = 1
>>> m['key'] = 2
>>> m
{'key': [1, 2]}

Это должно сделать это.

0 голосов
/ 06 декабря 2010

Вам не нужно.По определению, у dict может быть только одна запись для данного ключа.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...