dict - lambda filter python
¿Cómo filtrar un diccionario según una función de condición arbitraria? (7)
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 tu deseo de tener un método estupendo de dictionary.filter(f)
en una forma pitonica:
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 de filtro. Necesitamos usar .items()
para el filtrado, ya que el uso de .iteritems()
mientras se itera destructivamente generará una excepción.
Tengo un diccionario de puntos, por ejemplo:
>>> points={''a'':(3,4), ''b'':(1,2), ''c'':(5,5), ''d'':(3,3)}
Quiero crear un nuevo diccionario con todos los puntos cuyo valor xey sea menor que 5, es decir, los puntos ''a'', ''b'' y ''d''.
Según el libro , cada diccionario tiene la función items()
, que devuelve una lista de tupla (key, pair)
:
>>> 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 alguna función estupenda de dictionary.filter(f)
...
Hoy en día, en Python 2.7 en adelante, 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}
>>> 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)}
dict((k, v) for (k, v) in points.iteritems() if v[0] < 5 and v[1] < 5)
dict((k, v) for (k, v) in points.iteritems() if v[0] < 5 and v[1] < 5)
dict((k, v) for k, v in points.items() if all(x < 5 for x in v))
Puedes elegir invocar .iteritems()
lugar de .items()
si estás en Python 2 y los points
pueden tener muchas entradas.
all(x < 5 for x in v)
puede ser exagerado si usted está seguro de que cada punto siempre será solo 2D (en ese caso puede expresar la misma restricción con an and
) pero funcionará bien ;-).
points_small = dict(filter(lambda (a,(b,c)): b<5 and c < 5, points.items()))