2012-02-03 11 views
11

pracuję z list z dict obiektów, które wygląda następująco (kolejność obiektów różni):otrzymali listę słowników, w jaki sposób można wyeliminować duplikaty jednego klucza i sortować według innego

[ 
    {'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} 
] 

Co chcę zrobić, to usunąć zduplikowane nazwy, zachowując tylko jedno z tych nazw, które mają najwyższą wartość 'score'. Wyniki z powyższej listy będzie:

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

nie jestem pewien, który to wzór do wykorzystania tutaj (oprócz pozornie idiotycznego pętli, który utrzymuje sprawdzenie czy prąd dict „s 'name' jest już na liście, a następnie . sprawdzenie, czy jego 'score' jest wyższe niż istniejące czyjegoś 'score'

+3

Przejdź z pętli, to proste i jasne. –

+3

Jest to proste i przejrzyste i łatwe do odczytania w ciągu sześciu miesięcy, kiedy trzeba to zmienić "nieznacznie". –

+2

+1 Jest coś magicznego w tym pytaniu, ponieważ wymyślił różnorodny i interesujący zestaw odpowiedzi. Fascynujące jest, jak wiele całkowicie różnych rozwiązań ma ten problem. Biorę to pod uwagę jako ulubiony ze względu na bogaty zestaw odpowiedzi (również przechodzę do każdej odpowiedzi, która ma twórcze lub interesujące rozwiązanie). –

Odpowiedz

15

Jednym ze sposobów na to jest:

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()] 

więc wyjście będzie:

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

Wiele się o tym nauczyłem, dzięki – mVChr

3

Sortowanie to połowa sukcesu.

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 
+1

+1 Ta odpowiedź jest jedyną, która nie modyfikuje zestawu danych. Wygląda na spójne, a słowniki mogą mieć dodatkowe elementy, jeśli OP chce. – JBernardo

+1

+1 dla "Sortowanie to połowa sukcesu." –

+0

Jaki jest cel używania' object() 'tutaj? – fletom

2

Jest to najprostszy sposób mogę myśleć:

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'}] 

Osobiście nie wolą importować moduły gdy problemem jest zbyt mała.

2

Jeśli nie słyszeliście o grupie, to jest ładne korzystanie z niej:

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}] 
11

Nie ma potrzeby defaultdicts lub zestawów tutaj. Możesz po prostu użyć brudnych dyktatur i list.

Podsumuj najlepszy wynik pracuje w słowniku i przekonwertować wynik z powrotem do listy:

>>> 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'}] 
+1

To rozwiązanie byłoby najbardziej eleganckie, gdybyśmy używali struktury danych takiej jak '{'Foo': 3}' zamiast '[{'name': 'Foo', 'score': 3}]'. Powinien to robić plakat z oryginalnym pytaniem: – fletom

+1

To jest moje ulubione rozwiązanie, jedyne, co chciałbym zmienić to d.get (name, 0) na d.get (name, score), co pozwoliłoby również na negatywne wyniki. –

2

myślę, że mogę wymyślić jedną wkładką tutaj:

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

Nice. Jest to w pewnym stopniu czytelne dla jednego liniowca. –

5

Tylko zabawne, tutaj jest czysto funkcjonalne podejście:

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