Extraer elementos únicos de una lista de asignaciones
-
06-07-2019 - |
Pregunta
Es un problema interesante que busca la solución más pitónica. Supongamos que tengo una lista de asignaciones {'id': id, 'url': url}
. Algunos id
s en la lista están duplicados, y quiero crear una nueva lista, con todos los duplicados eliminados. Se me ocurrió la siguiente función:
def unique_mapping(map):
d = {}
for res in map:
d[res['id']] = res['url']
return [{'id': id, 'url': d[id]} for id in d]
Supongo que es bastante eficiente. ¿Pero hay un " más Pythonic " camino ? ¿O tal vez una forma más eficiente?
Solución
Su ejemplo se puede reescribir ligeramente para construir el primer diccionario utilizando una expresión generadora y eliminar la necesidad de construir otras asignaciones. Simplemente reutilice los viejos:
def unique_mapping(mappings):
return dict((m['id'], m) for m in mappings).values()
Aunque esto salió como una frase, todavía creo que es bastante legible.
Hay dos cosas que debe tener en cuenta al usar su solución original y la mía:
- los artículos no siempre serán devueltos en el mismo orden en que estaban originalmente
- la entrada posterior sobrescribirá las entradas anteriores con la misma identificación
Si no le importa, sugiero la solución anterior. En otro caso, esta función conserva el orden y trata los identificadores que se encuentran por primera vez con prioridad:
def unique_mapping(mappings):
addedIds = set()
for m in mappings:
mId = m['id']
if mId not in addedIds:
addedIds.add(mId)
yield m
Puede que necesite llamarlo con list (unique_mappings (mappings))
si necesita una lista y no un generador.
Otros consejos
Hay un par de cosas que podrías mejorar.
-
Estás realizando dos bucles, uno sobre el dict original, y luego nuevamente sobre el dict resultante. En su lugar, puede acumular sus resultados en un solo paso.
-
Puede cambiar para usar un generador, para evitar construir toda la lista por adelantado. (Use list (unique_mapping (items)) para convertir a una lista completa si la necesita)
-
No es necesario almacenar el valor cuando solo busca duplicados, puede usar un conjunto.
-
Estás recreando un diccionario para cada elemento, en lugar de devolver el original. Esto puede ser realmente necesario (por ejemplo, los está modificando y no quiere tocar el original), pero si no, es más eficiente usar los diccionarios ya creados.
Aquí hay una implementación:
def unique_mapping(items):
s = set()
for res in items:
if res['id'] not in s:
yield res
s.add(res['id'])
Creo que esto se puede simplificar aún más. Los diccionarios no toleran claves duplicadas. Haga su lista de asignaciones en un diccionario de asignaciones. Esto eliminará los duplicados.
>>> 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'}]