Estrazione di elementi univoci da un elenco di mappature
-
06-07-2019 - |
Domanda
È un problema interessante che cerca la soluzione più Pythonic. Supponiamo di avere un elenco di mappature {'id': id, 'url': url}
. Alcuni id
nell'elenco sono duplicati e desidero creare un nuovo elenco, con tutti i duplicati rimossi. Mi è venuta in mente la seguente funzione:
def unique_mapping(map):
d = {}
for res in map:
d[res['id']] = res['url']
return [{'id': id, 'url': d[id]} for id in d]
Suppongo sia abbastanza efficiente. Ma c'è un "più Pythonic" modo ? O forse un modo più efficiente?
Soluzione
Il tuo esempio può essere riscritto leggermente per costruire il primo dizionario usando un'espressione di generatore e per rimuovere la necessità di costruzione di un'altra mappatura. Riutilizza solo quelli vecchi:
def unique_mapping(mappings):
return dict((m['id'], m) for m in mappings).values()
Anche se questo è uscito come one-liner, penso ancora che sia abbastanza leggibile.
Ci sono due cose che devi tenere a mente quando usi la tua soluzione originale e la mia:
- gli articoli non saranno sempre restituiti nello stesso ordine in cui erano originariamente
- la voce successiva sovrascriverà le voci precedenti con lo stesso ID
Se non ti dispiace, allora suggerisco la soluzione sopra. In altri casi, questa funzione preserva l'ordine e tratta gli ID incontrati per primi con priorità:
def unique_mapping(mappings):
addedIds = set()
for m in mappings:
mId = m['id']
if mId not in addedIds:
addedIds.add(mId)
yield m
Potrebbe essere necessario chiamarlo con list (unique_mappings (mapping))
se hai bisogno di un elenco e non di un generatore.
Altri suggerimenti
Ci sono un paio di cose che potresti migliorare.
-
Stai eseguendo due loop, uno sul dict originale e poi di nuovo sul dict risultante. Invece, potresti creare i tuoi risultati in un solo passaggio.
-
Potresti cambiare per usare un generatore, per evitare di costruire l'intero elenco in anticipo. (Usa list (unique_mapping (items)) per convertire in un elenco completo se ne hai bisogno)
-
Non è necessario archiviare il valore quando si verifica solo la presenza di duplicati, è possibile utilizzare un set.
-
Stai ricreando un dizionario per ciascun elemento, anziché restituire l'originale. Questo potrebbe effettivamente essere necessario (ad es. Li stai modificando e non vuoi toccare l'originale), ma in caso contrario, è più efficiente usare i dizionari già creati.
Ecco un'implementazione:
def unique_mapping(items):
s = set()
for res in items:
if res['id'] not in s:
yield res
s.add(res['id'])
Penso che questo possa essere reso ancora più semplice. I dizionari non tollerano chiavi duplicate. Trasforma il tuo elenco di mappature in un dizionario di mappature. Ciò rimuoverà i duplicati.
>>> someListOfDicts= [
{'url': 'http://a', 'id': 'a'},
{'url': 'http://b', 'id': 'b'},
{'url': 'http://c', 'id': 'a'}]
>>> dict( [(x['id'],x) for x in someListOfDicts ] ).values()
[{'url': 'http://c', 'id': 'a'}, {'url': 'http://b', 'id': 'b'}]