web-dev-qa-db-fra.com

Comment filtrer un dictionnaire selon une fonction de condition arbitraire?

J'ai un dictionnaire de points, disons:

>>> points={'a':(3,4), 'b':(1,2), 'c':(5,5), 'd':(3,3)}

Je souhaite créer un nouveau dictionnaire avec tous les points dont les valeurs x et y sont inférieures à 5, c’est-à-dire les points "a", "b" et "d".

Selon le le livre , chaque dictionnaire possède la fonction items(), qui renvoie la liste de (key, pair) Tuple:

>>> points.items()
[('a', (3, 4)), ('c', (5, 5)), ('b', (1, 2)), ('d', (3, 3))]

Alors j'ai écrit ceci:

>>> for item in [i for i in points.items() if i[1][0]<5 and i[1][1]<5]:
...     points_small[item[0]]=item[1]
...
>>> points_small
{'a': (3, 4), 'b': (1, 2), 'd': (3, 3)}

Y a-t-il un moyen plus élégant? Je m'attendais à ce que Python ait une fonction super-géniale dictionary.filter(f) ...

184
Adam Matan

De nos jours, dans Python 2.7 et versions ultérieures, vous pouvez utiliser une compréhension dictée:

{k: v for k, v in points.iteritems() if v[0] < 5 and v[1] < 5}

Et en Python 3:

{k: v for k, v in points.items() if v[0] < 5 and v[1] < 5}
378
Thomas
dict((k, v) for k, v in points.items() if all(x < 5 for x in v))

Vous pouvez choisir d'appeler .iteritems() au lieu de .items() si vous êtes dans Python 2 et points pouvez avoir un lot d'entrées.

all(x < 5 for x in v) peut être exagéré si vous savez avec certitude que chaque point sera toujours en 2D uniquement (dans ce cas, vous pourriez exprimer la même contrainte avec un and), mais cela fonctionnera correctement ;-).

106
Alex Martelli
points_small = dict(filter(lambda (a,(b,c)): b<5 and c < 5, points.items()))
20
sizzzzlerz
>>> points = {'a': (3, 4), 'c': (5, 5), 'b': (1, 2), 'd': (3, 3)}
>>> dict(filter(lambda x: (x[1][0], x[1][1]) < (5, 5), points.items()))

{'a': (3, 4), 'b': (1, 2), 'd': (3, 3)}
12
benguesmia farid
dict((k, v) for (k, v) in points.iteritems() if v[0] < 5 and v[1] < 5)
9
nosklo

Je pense que la réponse d'Alex Martelli est certainement la manière la plus élégante de le faire, mais je voulais juste ajouter un moyen de satisfaire votre besoin pour une méthode super géniale dictionary.filter(f) dans une sorte de méthode pythonique:

class FilterDict(dict):
    def __init__(self, input_dict):
        for key, value in input_dict.iteritems():
            self[key] = value
    def filter(self, criteria):
        for key, value in self.items():
            if (criteria(value)):
                self.pop(key)

my_dict = FilterDict( {'a':(3,4), 'b':(1,2), 'c':(5,5), 'd':(3,3)} )
my_dict.filter(lambda x: x[0] < 5 and x[1] < 5)

Fondamentalement, nous créons une classe qui hérite de dict, mais ajoute la méthode de filtrage. Nous avons besoin d'utiliser .items() pour le filtrage, car utiliser .iteritems() lors d'une itération destructive soulève une exception.

7
qwwqwwq
dict((k, v) for (k, v) in points.iteritems() if v[0] < 5 and v[1] < 5)