Как отфильтровать словарь по произвольной условной функции?


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
Upvote! Это более чем в два раза быстрее, чем Мартеллис более общий подход. Обратите внимание, что вы также можете использовать представления (как итеитемы, они НЕ являются копиями элементов dict): {k: v для k, v в points.viewitems (), если v [0] <5 и v [1] < 5}
дорвак

5
И вот хорошее объяснение, почему вызов функции dict () медленнее синтаксиса конструктора / литерала {} doughellmann.com/2012/11/…
dorvak

1
Имейте в виду, что iteritemsбыло удалено в Python 3. Но вы можете использовать itemsвместо этого. Он ведет себя так, как iteritemsработает в старых версиях.
Элиас Замария

1
@Datanovice Я уверен, что можно. Можно также открыть новый вопрос с достаточной детализацией, чтобы получить более полезный ответ;)
Томас,

1
Кто-то открыл вопрос с ограниченными ответами, поэтому прибегнул к чтению как можно большего количества вопросов, чтобы лучше понять. Один видел более знающего и, таким образом, продолжал выбирать мозги;) Мой вопрос: 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ă

Вы сравниваете кортежи лексикографически, а это не то, что требуется для OP. В вашем случае, точка (3, 10)пройдет тест: (3, 10) < (5, 5)это правда, но это не так ( yдолжно быть меньше 5).
dmitry_romanov


7

Я думаю, что ответ Алекса Мартелли, безусловно, является самым элегантным способом сделать это, но я просто хотел добавить способ, чтобы удовлетворить вашу потребность в супер удивительном dictionary.filter(f)методе в стиле Pythonic:

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.