web-dev-qa-db-fra.com

Comprendre la liste, vérifier si l'élément est unique

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 ???]
30
Stefan Bossbaly

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})
34
geckon

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.

16
Willem Van Onsem

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']
7
m.wasowski

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

7
Bhargav Rao

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())
6
Thijs van Dien

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())))
5
Shashank

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)
4
nullptr

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.

4
Plug4