2010-05-16 29 views
125

que tienen un diccionario de puntos, dicen:Cómo filtrar un diccionario según una función de condición arbitraria?

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

que quieren crear un nuevo diccionario con todos los puntos cuyo valor X e Y es menor que 5, es decir, puntos 'a', 'b' y ' re'.

De acuerdo con la the book, 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 esta:

>>> 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 ? Yo estaba esperando a Python que tiene alguna función súper impresionante dictionary.filter(f) ...

+0

http://stackoverflow.com/questions/3420122/filter-dict-to-contain-only-certain-keys –

Respuesta

245

Hoy en día, en Python 2.7 y arriba, se puede utilizar 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} 
+6

Votación a favor! Esto es más de dos veces más rápido que el enfoque más general de Martellis. Tenga en cuenta que también puede usar vistas (como iteitems, NO son una copia de los elementos dict): {k: v para k, v en puntos.viewitems() si v [0] <5 yv [1] <5} – dorvak

+4

Y aquí hay una buena explicación de por qué la función call dict() es más lenta que la sintaxis constructor/literal {} http://doughellmann.com/ 2012/11/the-performance-impact-of-using-dict-instead-of-in-cpython-2-7-2.html – dorvak

+0

Ten en cuenta que 'iteritems' se eliminó en Python 3. Pero puedes usar' artículos' en su lugar. Se comporta como funciona 'iteritems' en versiones anteriores. –

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

ha olvidado() después de iteritems – KillianDS

+2

Necesita ** ** llamada al método, no solo ** mencionar ** it: poner '()' después del nombre del método 'iteritems'. –

+0

Whoops. Arreglado. –

8
dict((k, v) for (k, v) in points.iteritems() if v[0] < 5 and v[1] < 5) 
17
points_small = dict(filter(lambda (a,(b,c)): b<5 and c < 5, points.items())) 
+0

En Python 2 use iteritems() en lugar de items() – Regisz

+2

En python 3.5, esto devuelve un error: points_small = dict (filter (lambda (a, (b, c)): b <5 yc <5, points.items())) ^ SyntaxError: sintaxis no válida ' –

104
dict((k, v) for k, v in points.items() if all(x < 5 for x in v)) 

Usted puede optar por llamar .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 está seguro de que cada punto siempre será solo 2D (en ese caso podría expresar la misma restricción con un and) pero funcionará bien ;-).

7

creo que la respuesta de Alex Martelli es sin duda la forma más elegante para hacer esto, pero solo quería agregar una manera de satisfacer su deseo de un método dictionary.filter(f) súper impresionante en un tipo de manera Ptó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 de filtro. Necesitamos usar .items() para el filtrado, ya que usar .iteritems() mientras se itera de manera destructiva generará excepciones.

+0

+1 Gracias, código elegante. Realmente creo que debería ser parte del diccionario estándar. –

6
>>> 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)} 
Cuestiones relacionadas