Bir sözlüğü keyfi koşul işlevine göre nasıl filtreleyebilirim?


212

Bir nokta sözlüğü var, söyle:

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

X ve y değeri 5'ten küçük olan tüm noktaları içeren yeni bir sözlük oluşturmak istiyorum, yani 'a', 'b' ve 'd' noktalarını gösteriyor.

Göre kitapta , her sözlüğü vardır items()listesini döndürür işlevi, (key, pair) tuple:

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

Bu yüzden şunu yazdım:

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

Daha zarif bir yol var mı? Python'un süper harika bir dictionary.filter(f)işleve sahip olmasını bekliyordum ...


Yanıtlar:


427

Günümüzde, Python 2.7 ve sonraki sürümlerde, dik bir anlama kullanabilirsiniz:

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

Ve Python 3'te:

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

15
Oyla! Bu, Martellis'in daha genel yaklaşımından iki kat daha hızlıdır. Görünümleri de kullanabileceğinizi unutmayın (ite öğeler gibi, bunlar dikte edilen öğelerin bir kopyası DEĞİLDİR): {k: v için k, v, points.viewitems () içinde v [0] <5 ve v [1] < 5}
dorvak

5
Ve burada işlev çağrısı dict'inin () yapıcı / değişmez sözdiziminden daha yavaş olduğu iyi bir açıklama {} doughellmann.com/2012/11/…
dorvak

1
iteritemsPython 3'te kaldırıldığını unutmayın . Ancak itemsbunun yerine kullanabilirsiniz . iteritemsEski sürümlerde çalışma biçimini gösterir .
Elias Zamaria

1
@Datanovice Eminim ki olabilir. Daha yararlı bir cevap almak için yeterli ayrıntıya sahip yeni bir soru da açılabilir;)
Thomas

1
Biri sınırlı cevapları olan bir soru açmıştır, bu yüzden daha iyi bir anlayış elde etmek için olabildiğince çok soru okumaya başvurulmuştur. Biri daha bilgili bir tane gördü ve böylece, beyinleri seçmeye devam etti;) S: stackoverflow.com/questions/50104127/…
Datanovice

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

Sen buna tercih edebilirsiniz .iteritems()yerine .items()Python 2 iseniz ve pointsbir olabilir çok girişlerinin.

all(x < 5 for x in v)Her noktanın her zaman yalnızca 2B olacağından emin olabilirseniz aşırıya kaçabilir (bu durumda a ile aynı kısıtlamayı ifade edebilirsiniz and) ama iyi çalışır ;-).


21
points_small = dict(filter(lambda (a,(b,c)): b<5 and c < 5, points.items()))

1
Python 2'de items () yerine
iteritems

2
Python 3.5'te bu bir hata döndürür: points_small = dict (filtre (lambda (a, (b, c)): b <5 ve c <5, points.items ())) ^ Sözdizimi
Hatası

Bence python 3'te desteklenmiyor
matanster

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

3
harika ! lambda artık tuple argümanını açamayacağı için bu Py3 olduğundan bahsetmeye değer (bkz. PEP 3113 )
Ciprian Tomoiagă

Tuplleri sözlükbilimsel olarak karşılaştırıyorsunuz, bu OP'nin gerektirdiği şey değil. Sizin durumunuzda, nokta (3, 10)testi geçecektir: (3, 10) < (5, 5)Doğru, ancak yanlış ( y5'ten küçük olmalıdır).
dmitry_romanov

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

7

Alex Martelli'nin cevabının kesinlikle bunu yapmanın en zarif yolu olduğunu düşünüyorum, ancak sadece dictionary.filter(f)bir Pythonic tarzında süper harika bir yöntem için isteğinizi karşılamak için bir yol eklemek istedim :

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)

Temel olarak, devralınan dictancak filtre yöntemini ekleyen bir sınıf oluştururuz . .items()Filtreleme için kullanmamız gerekiyor , çünkü .iteritems()yıkıcı bir şekilde yinelenirken kullanmak istisnayı artıracaktır.


+1 Teşekkürler, zarif kod. Gerçekten standart sözlüğün bir parçası olması gerektiğini düşünüyorum.
Adam Matan

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.