Extraindo itens exclusivos de uma lista de mapeamentos
-
06-07-2019 - |
Pergunta
He're um problema interessante que procura a solução mais Pythonic. Suponha que eu tenho uma lista de mapeamentos {'id': id, 'url': url}
. Alguns id
s na lista são duplicados, e eu quero criar uma nova lista, com todas as duplicatas removidas. Eu vim com a seguinte função:
def unique_mapping(map):
d = {}
for res in map:
d[res['id']] = res['url']
return [{'id': id, 'url': d[id]} for id in d]
Acho que é bastante eficiente. Mas há uma "mais Pythonic" maneira? Ou talvez uma maneira mais eficiente?
Solução
O seu exemplo pode ser reescrita ligeiramente para construir o primeiro dicionário usando uma expressão gerador e para remover necessidade de construção de outros mapeamentos. Apenas reutilizar os antigos:
def unique_mapping(mappings):
return dict((m['id'], m) for m in mappings).values()
Embora este saiu como um one-liner, eu ainda acho que é muito legível.
Há duas coisas que você tem que manter em mente quando usar a sua solução original e mina:
- os itens não será sempre devolvido na mesma ordem em que foram originalmente
- a entrada mais tarde irá substituir entradas anteriores com o mesmo id
Se você não se importa, então eu sugiro a solução acima. Em outro caso, esta ordem e trata preserva a função ids de primeira encontrou com prioridade:
def unique_mapping(mappings):
addedIds = set()
for m in mappings:
mId = m['id']
if mId not in addedIds:
addedIds.add(mId)
yield m
Você pode precisar chamá-lo com list(unique_mappings(mappings))
se você precisar de uma lista e não um gerador.
Outras dicas
Há um par de coisas que você poderia melhorar.
-
Você está realizando dois loops, um sobre o dict original, e depois novamente sobre o dict resultado. Você poderia construir seus resultados em uma etapa em seu lugar.
-
Você poderia mudar para usar um gerador, para evitar a construção de toda a lista up-front. (Use lista (unique_mapping (itens)) para converter para uma lista completa se você precisar dele)
-
Não há nenhuma necessidade de armazenar o valor quando apenas a verificação de duplicatas, você pode usar um conjunto vez.
-
Você está recriando um dicionário para cada elemento, em vez de retornar o original. Isto pode ser realmente necessário (por exemplo. Você está modificando-os, e não quer tocar no original), mas se não for, é mais eficiente usar os dicionários já criadas.
Aqui está uma implementação:
def unique_mapping(items):
s = set()
for res in items:
if res['id'] not in s:
yield res
s.add(res['id'])
Eu acho que isso pode ser feito ainda mais simples. Dicionários não toleram chaves duplicadas. Faça a sua lista de mapeamentos em um dicionário de mapeamentos. Isto irá remover duplicatas.
>>> 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'}]