Избегайте вложенных циклов при проверке данных в Python

У меня есть два списка словарей:

dict_list1 = [{'k1':1, 'k2':2}, {'k1':3, 'k2':4}]
dict_list2 = [{'k1':1, 'k2':2, 'k3':10}, {'k1':3, 'k2':4, 'k3':10}]

И теперь для каждого dict_x в dict_list1 я хочу знать, есть ли dict_y в dict_list2, который содержит каждый ключ, значение из dict_x.

Я не могу придумать другого способа сделать это, кроме этого:

for dict_x in dict_list1:
    for dict_y in dict_list2:
        count = len(dict_x)
        for key, val in dict_x.items():
            if key in dict_y and dict_y[key] == val:
                count -= 1
        if count == 0:
            print('YAY')
            break
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
0
88
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Вы можете использовать any и all:

dict_list1 = [{'k1':1, 'k2':2}, {'k1':3, 'k2':4}]
dict_list2 = [{'k1':1, 'k2':2, 'k3':10}, {'k1':3, 'k2':4, 'k3':10}]
v = [any(all(c in i and i[c] == k for c, k in b.items()) for i in dict_list2)\
   for b in dict_list1]

Выход:

[True, True]

Это проверяет только ключи, но не значения.

Patrick Haugh 08.08.2018 22:31

Кроме того, разве у него нет нескольких петель, просто вставленных в однострочник?

roganjosh 08.08.2018 22:33

Да, я полагаю, есть еще 3 форса, но в одной строке

GuiFGDeo 08.08.2018 22:34

Попробуйте dict_list1 = [{'k1':1, 'k2': None}] и dict_list2 = [{'k1':1}].

Patrick Haugh 08.08.2018 22:35

Ниже я использую тот факт, что Представление dict.items реализует операции set для проверки каждого d1.items(), существует ли d2.items(), так что d1.items() является подмножеством d2.items().

[any(d1.items() <= d2.items() for d2 in dict_list2) for d1 in dict_list1]

Моя текущая версия возвращает список длиной dict_list1, представляющий, является ли какой-либо элемент dict_list2 суперсредством словаря по этому индексу в dict_list1.

Patrick Haugh 08.08.2018 22:40
Ответ принят как подходящий

Представления dict могут выполнять быстрое тестирование "подмножества" с помощью операторов неравенства. Так:

if dict_x.items() <= dict_y.items():  # Use .viewitems() instead of .items() on Python 2.7

вернет истину только в том случае, если каждая пара ключ / значение в dict_x также присутствует в dict_y.

Это ничего не изменит с точки зрения производительности big-O, но сделает код несколько чище:

for dict_x in dict_list1:
    for dict_y in dict_list2:
        if dict_x.items() <= dict_y.items():
            print('YAY')
            break

Обратите внимание, что создание представлений стоит что-то (это просто фиксированная стоимость, не зависящая от размера dict), поэтому, если производительность имеет значение, возможно, стоит кэшировать представления; для dict_list1 это бесплатно:

for dict_x in dict_list1:
    dict_x_view = dict_x.items()
    for dict_y in dict_list2:
        if dict_x_view <= dict_y.items():
            print('YAY')
            break

но для кеширования обоих потребуются некоторые активные преобразования:

# Convert all of dict_list2 to views up front; costs a little if
# not all views end up being tested (we always break before finishing)
# but usually saves some work at the cost of a tiny amount of memory
dict_list2_views = [x.items() for x in dict_list2]
for dict_x in dict_list1:
    dict_x_view = dict_x.items()
    for dict_y_view in dict_list2_views:
        if dict_x_view <= dict_y_view:
            print('YAY')
            break

Вы также можете свернуть цикл с помощью any (что устраняет необходимость в break из-за короткого замыкания any), поэтому первая (простейшая) проверка может выглядеть так:

for dict_x in dict_list1:
    if any(dict_x.items() <= dict_y.items() for dict_y in dict_list2):
       print('YAY')

В дальнейшем это можно было бы свести к единому списку, который приводит к различным совпадениям, но в этот момент код будет довольно тесным / уродливым:

for _ in (dict_x in dict_list1 if any(dict_x.items() <= dict_y.items() for dict_y in dict_list2)):
    print('YAY')

хотя, не зная, что бы вы на самом деле сделали (а не просто печатать YAY), это становится немного бессмысленным.

Принятие ответа @ShadowRanger, потому что он более полный.

GuiFGDeo 08.08.2018 22:51

Другие вопросы по теме