Учитывая список словарей, как я могу устранить дубликаты одного ключа и сортировать по другому

Я работаю с объектами list из dict, которые выглядят следующим образом (порядок объектов отличается):

[
    {'name': 'Foo', 'score': 1},
    {'name': 'Bar', 'score': 2},
    {'name': 'Foo', 'score': 3},
    {'name': 'Bar', 'score': 3},
    {'name': 'Foo', 'score': 2},
    {'name': 'Baz', 'score': 2},
    {'name': 'Baz', 'score': 1},
    {'name': 'Bar', 'score': 1}
]

Что я хочу сделать, это удалить повторяющиеся имена, сохраняя только одно имя, имеющее наивысшее значение 'score'. Результаты из приведенного выше списка будут следующими:

[
    {'name': 'Baz', 'score': 2},
    {'name': 'Foo', 'score': 3},
    {'name': 'Bar', 'score': 3}
]

Я не уверен, какой шаблон использовать здесь (кроме, казалось бы, идиотского цикла, который продолжает проверять, находится ли текущий dict 'name' в списке, а затем проверяет, находится ли его 'score' выше существующего один 'score'.

Ответ 1

Один из способов сделать это:

data = collections.defaultdict(list)
for i in my_list:
    data[i['name']].append(i['score'])
output = [{'name': i, 'score': max(j)} for i,j in data.items()]

поэтому вывод будет:

[{'score': 2, 'name': 'Baz'},
 {'score': 3, 'name': 'Foo'},
 {'score': 3, 'name': 'Bar'}]

Ответ 2

Здесь нет необходимости в defaultdicts или наборах. Вы можете просто использовать простые просты и списки.

Подведите итоговый результат в словаре и преобразуйте результат обратно в список:

>>> s = [
    {'name': 'Foo', 'score': 1},
    {'name': 'Bar', 'score': 2},
    {'name': 'Foo', 'score': 3},
    {'name': 'Bar', 'score': 3},
    {'name': 'Foo', 'score': 2},
    {'name': 'Baz', 'score': 2},
    {'name': 'Baz', 'score': 1},
    {'name': 'Bar', 'score': 1}
]
>>> d = {}
>>> for entry in s:
        name, score = entry['name'], entry['score']
        d[name] = max(d.get(name, 0), score)

>>> [{'name': name, 'score': score} for name, score in d.items()]
[{'score': 2, 'name': 'Baz'}, {'score': 3, 'name': 'Foo'}, {'score': 3, 'name': 'Bar'}]

Ответ 3

Просто для удовольствия, здесь есть чисто функциональный подход:

>>> map(dict, dict(sorted(map(sorted, map(dict.items, s)))).items())
[{'score': 3, 'name': 'Bar'}, {'score': 2, 'name': 'Baz'}, {'score': 3, 'name': 'Foo'}]

Ответ 4

Сортировка - это половина битвы.

import itertools
import operator

scores = [
    {'name': 'Foo', 'score': 1},
    {'name': 'Bar', 'score': 2},
    {'name': 'Foo', 'score': 3},
    {'name': 'Bar', 'score': 3},
    {'name': 'Foo', 'score': 2},
    {'name': 'Baz', 'score': 2},
    {'name': 'Baz', 'score': 1},
    {'name': 'Bar', 'score': 1}
]

result = []
sl = sorted(scores, key=operator.itemgetter('name', 'score'),
  reverse=True)
name = object()
for el in sl:
  if el['name'] == name:
    continue
  name = el['name']
  result.append(el)
print result

Ответ 5

Это самый простой способ, о котором я могу думать:

names = set(d['name'] for d in my_dicts)
new_dicts = []
for name in names:
    d = dict(name=name)
    d['score'] = max(d['score'] for d in my_dicts if d['name']==name)
    new_dicts.append(d)

#new_dicts
[{'score': 2, 'name': 'Baz'},
 {'score': 3, 'name': 'Foo'},
 {'score': 3, 'name': 'Bar'}]

Лично я предпочитаю не импортировать модули, когда проблема слишком мала.

Ответ 6

Если вы не слышали о группе, это приятно использовать:

from itertools import groupby

data=[
    {'name': 'Foo', 'score': 1},
    {'name': 'Bar', 'score': 2},
    {'name': 'Foo', 'score': 3},
    {'name': 'Bar', 'score': 3},
    {'name': 'Foo', 'score': 2},
    {'name': 'Baz', 'score': 2},
    {'name': 'Baz', 'score': 1},
    {'name': 'Bar', 'score': 1}
]

keyfunc=lambda d:d['name']
data.sort(key=keyfunc)

ans=[]
for k, g in groupby(data, keyfunc):
    ans.append({k:max((d['score'] for d in g))})
print ans

>>>
[{'Bar': 3}, {'Baz': 2}, {'Foo': 3}]

Ответ 7

Думаю, я могу придумать однострочный:

result = dict((x['name'],x) for x in sorted(data,key=lambda x: x['score'])).values()