Cómo filtrar un diccionario según una función de condición arbitraria?


Tengo un diccionario de puntos, decir:

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

Quiero crear un nuevo diccionario con todos los puntos cuyo valor x e y es menor que 5, es decir, los puntos 'a', 'b' y 'd'.

De acuerdo con el el libro , cada diccionario tiene la función items(), que devuelve una lista de (key, pair) tupla:

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

Así que he escrito esto:

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

¿Hay una manera más elegante? Esperaba que Python tuviera una función dictionary.filter(f) súper impresionante...

7 answers

Hoy en día, en Python 2.7 y arriba, puedes usar una comprensión dict:

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

Y en Python 3:

{k: v for k, v in points.items() if v[0] < 5 and v[1] < 5}
 286
Author: Thomas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-04-27 12:13:42
dict((k, v) for k, v in points.items() if all(x < 5 for x in v))

Puedes elegir llamar a .iteritems() en lugar de .items() si estás en Python 2 y points puede tener un lote de entradas.

all(x < 5 for x in v) puede ser exagerado si sabe con seguridad que cada punto siempre será 2D solamente (en ese caso puede expresar la misma restricción con un and) pero funcionará bien;-).

 105
Author: Alex Martelli,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-05-16 16:37:47
points_small = dict(filter(lambda (a,(b,c)): b<5 and c < 5, points.items()))
 17
Author: sizzzzlerz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-05-16 16:35:56
dict((k, v) for (k, v) in points.iteritems() if v[0] < 5 and v[1] < 5)
 8
Author: nosklo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-05-16 16:35:19
>>> 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)}
 8
Author: benguesmia farid,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-12-11 13:02:23

Creo que la respuesta de Alex Martelli es definitivamente la forma más elegante de hacer esto, pero solo quería agregar una manera de satisfacer su deseo de un método súper impresionante dictionary.filter(f) de una manera pitónica:

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)

Básicamente creamos una clase que hereda de dict, pero agrega el método filter. Necesitamos usar .items() para el filtrado, ya que usar .iteritems() mientras se itera destructivamente generará excepción.

 7
Author: qwwqwwq,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-02-03 22:23:01
dict((k, v) for (k, v) in points.iteritems() if v[0] < 5 and v[1] < 5)
 5
Author: Ignacio Vazquez-Abrams,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-05-16 16:39:17