2012-02-03 16 views
11

Sto lavorando con un list di dict oggetti che assomiglia a questo (l'ordine degli oggetti è diversa):Dato un elenco di dizionari, come posso eliminare i duplicati di una chiave, e ordinare da un altro

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

Quello che voglio fare è rimuovere i nomi duplicati, mantenendo solo quello di ciascun nome che ha il più alto 'score'. I risultati della lista di cui sopra sarebbero:

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

Non sono sicuro di quale modello da utilizzare qui (a parte un ciclo apparentemente idiota che continua a controllare se l'attuale dict s' 'name' è nella lista già e poi . Verificando la sua 'score' è superiore al già esistente 'score'

+3

Vai con il ciclo, è semplice e chiaro. –

+3

È semplice e chiaro e facile da leggere in sei mesi quando è necessario cambiarlo "leggermente" –

+2

+1 C'è qualcosa di magico in questa domanda in quanto ha suscitato un insieme di risposte diverse e interessanti. È affascinante quante soluzioni completamente diverse abbia questo problema. Lo considero un favorito a causa del ricco set di risposte (sto anche inviando a monte ogni risposta che ha una soluzione creativa o interessante). –

risposta

15

un modo proprio per farlo è:

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

così output sarà:

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

Da questo ho imparato molto su Python, grazie – mVChr

3

L'ordinamento è metà della battaglia.

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 Questa risposta è l'unica che non trasforma il dataset. Sembra coerente e i dizionari possono avere oggetti extra se OP vuole. – JBernardo

+1

+1 per "L'ordinamento è metà della battaglia." –

+0

Qual è lo scopo dell'uso di' object() 'qui? – fletom

2

Questo è il modo più semplice che posso pensare:

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

Personalmente, preferisco non importare i moduli quando il problema è troppo piccolo.

2

Nel caso in cui non avete sentito parlare di gruppo da, questo è bello usarlo:

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

Non c'è bisogno di defaultdicts o set qui. Puoi semplicemente usare semplici dict e liste.

riassumere il miglior punteggio in esecuzione in un dizionario e convertire il risultato di nuovo in una lista:

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

Questa soluzione sarebbe la più elegante se usassimo una struttura dati come '{'Foo': 3}' invece di '[{'name': 'Foo', 'score': 3}]'. Direi che il poster della domanda originale dovrebbe farlo – fletom

+1

Questa è la mia soluzione preferita.L'unica cosa che cambierei è d.get (nome, 0) per d.get (nome, punteggio). Ciò consentirebbe anche punteggi negativi. –

2

credo di poter venire con un one-liner qui:

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

Bello, questo è in qualche modo leggibile per una linea singola. –

5

Solo per divertente, ecco un approccio puramente funzionale:

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