Как отфильтровать два списка dict в Python 3.8

#python-3.x #algorithm

#python-3.x #алгоритм

Вопрос:

Простите меня, если тема уже существует, но я ее не нашел…

У меня есть 3 списка dict :

 list_1 = [
    {'name': "Leonardo Di Caprio", 'films': ["The revenant", "Titanic", "The wold of Wall Street"]},
    {'name': "Will Smith", 'films': ["I am a legend", "The pursuit of happyness"]},
    {'name': "Robert De Niro", 'films': ["Taxi driver", "The godfather"]}
]

list_2 = [
    {'name': "Leonardo Di Caprio", 'films': ["Titanic", "The revenant", "The wold of Wall Street"]},
    {'name': "Will Smith", 'films': ["I am a legend", "The pursuit of happyness", "Aladdin"]},
    {'name': "Robert De Niro", 'films': ["Taxi driver", "The godfather"]}
]

list_final = [
    {'name': "Tom Hanks", 'films': ["Forest Gump", "Cast Away", "Greyhound"]},
    {'name': "Will Smith", 'films': ["I am a legend", "The pursuit of happyness"]},
    {'name': "Tom Cruise", 'films': ["Top Gun", "Mission impossible"]},
    {'name': "Robert De Niro", 'films': ["Taxi driver", "The godfather"]},
    {'name': "Leonardo Di Caprio", 'films': ["Titanic", "The revenant", "The wold of Wall Street"]},
    {'name': "Harrison Ford", 'films': ["Blade Runner", "Indiana Jones"]},
    {'name': "Morgan Freeman", 'films': ["Seven"]}
]
 

Я хотел бы создать функцию, которая принимает 2 списка dict в качестве параметров и возвращает логическое значение. Цель состоит в том, чтобы проверить list_1 , содержится ли в list_final .
Под «содержится» я имею в виду :

  • В каждом актере list_1 должны присутствовать имена froom list_final (независимо от порядка)
  • Все фильмы, в которых играет определенный актер, list_1 должны присутствовать в list_final

У меня есть функциональный код :

 def isContained(l1 : List[Dict[str, List]], l_final: List[Dict[str, List]]) -> bool:

    for elem in l1:
        findOccurence = False
        for element in l_final:
            if elem['name'] == element['name'] and all(item in element['films'] for item in elem['films']):
                findOccurence = True
        if not findOccurence:
            return False
    return True

print(isContained(list_1, list_final)) # True
print(isContained(list_2, list_final)) # False
print(isContained(list_1, list_2)) # True
print(isContained(list_2, list_1)) # False
 

Вывод :

 root@root:/tmp/TEST_PYTHON$ python3 main.py
True
False
True
False
 

Итак, это работает, но я уверен, что есть другой способ закодировать его в более оптимизированном алгоритме.
Что меня беспокоит, так это повторение всего окончательного списка столько раз, сколько у меня итераций в list_1

Есть предложения?

Комментарии:

1. Обратите внимание, что слово «функциональный» имеет очень специфическое значение в программировании. Я бы не стал описывать ваш код как «функциональный».

2. Это правда, я хотел сказать «рабочий» код, спасибо за совет. Это лучше?

Ответ №1:

После небольшой корректировки ваших структур данных, чтобы сделать работу немного более эффективной…

Это можно сделать, используя оператор intersect amp; и метод issubset для set .

 list_1 = {
     "Leonardo Di Caprio":{"The revenant", "Titanic", "The wold of Wall Street"},
     "Will Smith":{"I am a legend", "The pursuit of happyness"},
     "Robert De Niro": {"Taxi driver", "The godfather"}
}

list_2 = {
    "Leonardo Di Caprio": {"Titanic", "The revenant", "The wold of Wall Street"},
    "Will Smith": {"I am a legend", "The pursuit of happyness", "Aladdin"},
    "Robert De Niro": {"Taxi driver", "The godfather"}
}

list_final = {
    "Tom Hanks": {"Forest Gump", "Cast Away", "Greyhound"},
    "Will Smith": {"I am a legend", "The pursuit of happyness"},
    "Tom Cruise": {"Top Gun", "Mission impossible"},
    "Robert De Niro": {"Taxi driver", "The godfather"},
    "Leonardo Di Caprio": {"Titanic", "The revenant", "The wold of Wall Street"},
    "Harrison Ford": {"Blade Runner", "Indiana Jones"},
    "Morgan Freeman": {"Seven"}
}


def isContained(l1, l_final) -> bool:
    if (set(l1.keys()).issubset(set(l1.keys()))):
        for key in set(l1.keys()) amp; set(l_final.keys()):
            if (not (l1[key].issubset(l_final[key]))):
                return False;
    return True;
 

После комментариев от @Stef решение было исправлено и теперь:

 def isContained(l1, l_final) -> bool:
    if (set(l1.keys()).issubset(set(l_final.keys()))):
        for key in set(l1.keys()) amp; set(l_final.keys()):
            if (not (l1[key].issubset(l_final[key]))):
                return False;
    else:
        return False
    return True;
 

Для подтверждения правильного выполнения первого условия требуется дополнительное условие проверки…

 list_3 = {"Sigourney Weaver": {"Aliens"}}
 

И условие:

 print(isContained(list_3, list_final)) # False
 

Комментарии:

1. set(l1.keys()).issubset(set(l1.keys())) Я думаю, что один из них должен быть l_final , а не l1 ?

2. Существует и другая ошибка — если условие внешнего if равно false , то isContained возвращается True . Но False в этом случае он должен вернуться, верно?

3. Вы могли бы переписать все это как def isContained(l1, l2): return (set(l1.keys()).issubset(l2.keys())) and all(set(v).issubset(l2[k]) for k,v in l1.items())

4. Вы также можете завершить свой ответ, предоставив функцию для преобразования формата списка OP в формат вашего dict: def dict_of_list(l): return {x['name']: set(x['films']) for x in l}

5. Я не вижу точки оператора пересечения amp; в цикле for; на данный момент вы уже проверили, что l1.keys() это подмножество l_final.keys() , поэтому из этого следует, что set(l1.keys()) amp; set(l_final.keys()) == set(l1.keys()) .