J'essaie d'écrire une déclaration de compréhension de liste qui n'ajoutera un élément que s'il n'est pas actuellement contenu dans la liste. Existe-t-il un moyen de vérifier les éléments actuels de la liste en cours de construction? Voici un bref exemple:
entrée
{
"Stefan" : ["running", "engineering", "dancing"],
"Bob" : ["dancing", "art", "theatre"],
"Julia" : ["running", "music", "art"]
}
Sortie
["running", "engineering", "dancing", "art", "theatre", "music"]
Code sans utiliser de compréhension de liste
output = []
for name, hobbies in input.items():
for hobby in hobbies:
if hobby not in output:
output.append(hobby)
Ma tentative
[hobby for name, hobbies in input.items() for hobby in hobbies if hobby not in ???]
Vous pouvez utiliser set
et définir la compréhension:
{hobby for name, hobbies in input.items() for hobby in hobbies}
Comme m.wasowski mentionné , nous n'utilisons pas le name
ici, nous pouvons donc utiliser item.values()
à la place:
{hobby for hobbies in input.values() for hobby in hobbies}
Si vous avez vraiment besoin d'une liste comme résultat, vous pouvez le faire (mais notez que vous pouvez généralement travailler avec des ensembles sans aucun problème):
list({hobby for hobbies in input.values() for hobby in hobbies})
Comme cette réponse le suggère: vous pouvez utiliser un filtre d'unicité:
def f7(seq):
seen = set()
seen_add = seen.add
return [x for x in seq if not (x in seen or seen_add(x))]
et appelez avec:
>>> f7(hobby for name, hobbies in input.items() for hobby in hobbies)
['running', 'engineering', 'dancing', 'art', 'theatre', 'music']
Je voudrais implémenter le filtre d'unicité séparément car une règle de conception dit "différentes choses doivent être gérées par différentes classes/méthodes/composants/quoi que ce soit ". De plus, vous pouvez simplement réutiliser cette méthode si nécessaire.
Un autre avantage est - comme cela est écrit à la réponse liée - que l'ordre des éléments est préservé. Pour certaines applications, cela peut être nécessaire.
les ensembles et les dictionnaires sont vos amis ici:
from collections import OrderedDict
from itertools import chain # 'flattens' collection of iterables
data = {
"Stefan" : ["running", "engineering", "dancing"],
"Bob" : ["dancing", "art", "theatre"],
"Julia" : ["running", "music", "art"]
}
# using set is the easiest way, but sets are unordered:
print {hobby for hobby in chain.from_iterable(data.values())}
# output:
# set(['art', 'theatre', 'dancing', 'engineering', 'running', 'music'])
# or use OrderedDict if you care about ordering:
print OrderedDict(
(hobby, None) for hobby in chain.from_iterable(data.values())
).keys()
# output:
# ['dancing', 'art', 'theatre', 'running', 'engineering', 'music']
Si vous voulez vraiment vraiment un listcomp et seulement un list-comp, vous pouvez le faire
>>> s = []
>>> [s.append(j) for i in d.values() for j in i if j not in s]
[None, None, None, None, None, None]
>>> s
['dancing', 'art', 'theatre', 'running', 'engineering', 'music']
Ici, s
est le résultat d'un effet secondaire et d
est votre dictionnaire d'origine. L'avantage unique ici est que vous pouvez conserver l'ordre contrairement à la plupart des autres réponses ici .
Note: C'est un mauvaise façon car il exploite la liste-composition et le résultat est un effet secondaire. Ne le faites pas comme une pratique, cette réponse est juste pour vous montrer que vous pouvez y parvenir en utilisant une liste de composition seule
Il existe une autre façon d'écrire ceci qui est un peu plus descriptif de ce que vous faites réellement, et ne nécessite pas de compréhension imbriquée (double for
):
output = set.union(*[set(hobbies) for hobbies in input_.values()])
Cela devient encore plus agréable lorsque vous représentez l'entrée pour être plus conceptuellement saine, c'est-à-dire utiliser des ensembles pour les passe-temps de chaque personne (car il ne devrait pas y avoir de répétitions non plus):
input_ = {
"Stefan" : {"running", "engineering", "dancing"},
"Bob" : {"dancing", "art", "theatre"},
"Julia" : {"running", "music", "art"}
}
output = set.union(*input_.values())
Une compréhension de la liste n'est pas bien adaptée à ce problème. Je pense qu'une compréhension d'ensemble serait mieux, mais comme cela a déjà été montré dans une autre réponse, je vais montrer un moyen de résoudre ce problème avec un monoligne compact:
list(set(sum(hobbies_dict.values(), [])))
Une autre solution intéressante utilisant bitwise ou operator qui sert d'opérateur d'union pour les ensembles:
from operator import or_
from functools import reduce # Allowed, but unnecessary in Python 2.x
list(reduce(or_, map(set, hobbies_dict.values())))
Ou (jeu de mots involontaire, je le jure), au lieu d'utiliser l'opérateur au niveau du bit ou l'opérateur, utilisez simplement set.union
et passez-lui le set-mapping décompressé de vos valeurs. Pas besoin d'importer or_
et reduce
! Cette idée est inspirée de réponse de Thijs van Dien .
list(set.union(*map(set, hobbies_dict.values())))
Utilisez un ensemble:
dict = {
"Stefan" : ["running", "engineering", "dancing"],
"Bob" : ["dancing", "art", "theatre"],
"Julia" : ["running", "music", "art"]
}
myset = set()
for _, value in dict.items():
for item in value:
myset.add(item)
print(myset)
Que dis-tu de ça:
set(dict['Bob']+dict['Stefan']+dict['Julia'])
>>> set(['art', 'theatre', 'dancing', 'engineering', 'running', 'music'])
Ou plus joliment:
dict = {
"Stefan" : ["running", "engineering", "dancing"],
"Bob" : ["dancing", "art", "theatre"],
"Julia" : ["running", "music", "art"]
}
list_ = []
for y in dict.keys():
list_ = list_ + dict[y]
list_ = set(list_)
>>> list_
set(['art', 'theatre', 'dancing', 'engineering', 'running', 'music'])
vous pouvez appliquer la fonction list
à list_ comme list(list_)
pour renvoyer une liste plutôt qu'un ensemble.