Як відфільтрувати словник за функцією довільної умови?


212

У мене є словник балів, скажімо:

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

Я хочу створити новий словник з усіма точками, значення x і y менше 5, тобто точки 'a', 'b' і 'd'.

Відповідно до книги , кожен словник має items()функцію, яка повертає список (key, pair) кортежу:

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

Тому я написав це:

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

Чи є більш елегантний спосіб? Я очікував, що Python матиме якусь надзвичайну dictionary.filter(f)функцію ...


Відповіді:


427

Сьогодні в Python 2.7 і новіших версіях ви можете використовувати розуміння dict:

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

А в Python 3:

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

15
Оновлення! Це більш ніж у два рази швидше, ніж загальний підхід Мартелліса. Зверніть увагу, що ви також можете використовувати представлення даних (як iteitems, вони НЕ є копією елементів dict): {k: v для k, v у пунктах.viewitems (), якщо v [0] <5 і v [1] < 5}
дорвак

5
І ось хороше пояснення, чому функція dict () повільніше, ніж синтаксис конструктор / буквальний {} doughellmann.com/2012/11/…
dorvak

1
Майте на увазі, що iteritemsбуло видалено в Python 3. Але ви можете використовувати itemsзамість цього. Він поводиться так, як iteritemsпрацює у старих версіях.
Elias Zamaria

1
@Datanovice Я впевнений, що міг би. Можна також відкрити нове запитання з достатньою деталізацією, щоб отримати кориснішу відповідь;)
Томас

1
Одне відкрило питання з обмеженими відповідями, таким чином, вдалося прочитати стільки питань, скільки можна, щоб краще зрозуміти. Одна пила більш обізнаними один і , таким чином, по- як і раніше вибрати ті мізки;) мій Q: stackoverflow.com/questions/50104127 / ...
Datanovice

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

Ви можете вибрати дзвінок .iteritems()замість того, .items()якщо ви перебуваєте на Python 2 і, pointsможливо, є багато записів.

all(x < 5 for x in v)може бути зайвим, якщо ви точно знаєте, кожна точка завжди буде лише 2D (у такому випадку ви можете висловити одне і те ж обмеження and), але це буде добре ;-).


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

1
У Python 2 використовуйте iteritems () замість items ()
Regisz

2
У python 3.5 це повертає помилку: points_small = dict (фільтр (лямбда (a, (b, c))): b <5 і c <5, points.items ())) ^ SyntaxError: недійсний синтаксис `
Мевін Бабу

Я думаю, що це не підтримується в python 3
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
чудово! Варто згадати, що це Py3, оскільки лямбда вже не може розпакувати аргумент кортежу (див. PEP 3113 )
Ciprian Tomoiagă

Ви порівнюєте кортежі лексикографічно, а це не те, що потрібно для ОП. У вашому випадку (3, 10)тест пройде тест: (3, 10) < (5, 5)це True, але це неправильно (також yмає бути менше 5).
dmitry_romanov


7

Я думаю, що відповідь Алекса Мартеллі - це, безумовно, найелегантніший спосіб зробити це, але просто хотілося додати спосіб задоволення вашого бажання надзвичайно дивовижним dictionary.filter(f)методом в пітонічному вигляді:

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)

В основному ми створюємо клас, який успадковує dict, але додає метод фільтра. Нам потрібно використовувати .items()для фільтрації, оскільки використання .iteritems()під час деструктивної ітерації призведе до виключення.


+1 Дякую, елегантний код. Я дійсно думаю, що це має бути частиною стандартного словника.
Адам Матан

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.