Страницы

Поиск по вопросам

воскресенье, 8 декабря 2019 г.

Пересечение списков словарей по определенному значению ключа. Необходима оптимизация кода

#python #python_3x #python_2x #списки #словари


Есть два списка словарей:


types - содержит информацию о названиях спортивных секциях и датах их посещения одного
посетителя.
user_list - содержит информацию о людях которые тоже ходили в этот спортзал и даты
их посещения.

types = [{'type':'tango', 'date':'200118'}, 
{'type':'box', 'date':'110217'},
{'type':'strip', 'date':'170518'}]

user_list = [{'name':'maxim', 'date':'200118'},
{'name':'ivan', 'date':'110217'},
{'name':'elena', 'date':'051018'},
{'name':'gleb', 'date':'170518'},  
{'name':'magas', 'date':'110217'},  
{'name':'ivan', 'date':'170518'}, 
{'name':'maxim', 'date':'110217'}]



Задача

Необходимо из второго списка выбрать посетителей которые были в одни и те же даты
с тем, кто указан в первом списке. И ещё условие: что были они в те же даты более одного
раза. 

Результат

В итоге должен получится вот такой список:

[{'date': '110217', 'name': 'ivan'}, {'date': '170518', 'name': 'ivan'}, {'date':
'200118', 'name': 'maxim'}, {'date': '110217', 'name': ‘maxim'}]


Если по другому объяснить, то нужно из второго списка выбрать строки значения полей
‘дата’, которые присутствуют в первом списке и встречаются во втором более одного раза.
Ниже представлено мое решение, которое мне ужасно не нравится. Часть кода можно заменить
на генераторы, но может быть кто нибудь знает более оптимальные инструменты?

types = [ {'type':'tango', 'date':'200118'},
{'type':'box', 'date':'110217'},
{'type':'strip', 'date':'170518'}]

user_list = [{'name':'maxim', 'date':'200118'},
{'name':'ivan', 'date':'110217'},
{'name':'elena', 'date':'051018'},
{'name':'gleb', 'date':'170518'},
{'name':'magas', 'date':'110217'},
{'name':'ivan', 'date':'170518'},
{'name':'maxim', 'date':'110217'}]

dates = [dct['date'] for dct in types]
names = list(set([dct['name'] for dct in user_list]))


goodusers = []
for name in names:
    res = []
    for u in user_list:
        if u['name']==name and u['date'] in dates:
            res.append(u)
    if len(res)>1:
        goodusers += res

print(goodusers)

    


Ответы

Ответ 1



Вы начали верно. Только нет смысла извлекать отдельно имена. from itertools import groupby dates = [dct['date'] for dct in types] # фильтруем по совпадению дат users = (d for d in user_list if d['date'] in dates) # сортируем, для работы функции группировки key_name = lambda u: u['name'] users = sorted(users, key=key_name) double_users = [] for _, u in groupby(users, key=key_name): pool = tuple(u) if len(pool) > 1: double_users += pool print(double_users)

Ответ 2



Такие задачи удобно решать используя модуль Pandas: import pandas as pd # pip install pandas сначала создадим DataFrame's из списков словарей: u = pd.DataFrame(user_list) t = pd.DataFrame(types) получились след. DataFrame's: In [59]: u Out[59]: date name 0 200118 maxim 1 110217 ivan 2 051018 elena 3 170518 gleb 4 110217 magas 5 170518 ivan 6 110217 maxim In [60]: t Out[60]: date type 0 200118 tango 1 110217 box 2 170518 strip теперь их можно отфильтровать одной коммандой: res = u.merge(t[['date']]).sort_values('name').groupby('name').filter(lambda x: len(x) > 1) результат: In [64]: res Out[64]: date name 1 110217 ivan 5 170518 ivan 0 200118 maxim 3 110217 maxim тоже самое в виде списка словарей: In [67]: res.to_dict('records') Out[67]: [{'date': '110217', 'name': 'ivan'}, {'date': '170518', 'name': 'ivan'}, {'date': '200118', 'name': 'maxim'}, {'date': '110217', 'name': 'maxim'}]

Ответ 3



Ну тогда вот, благодаря автору вопроса и его гостю: import sqlite3 as li3 conn = li3.connect(":memory:") cursor = conn.cursor() cursor.execute("CREATE TABLE visits (name TEXT, date TEXT)") cursor.execute("CREATE TABLE types ( type TEXT, date TEXT)") cursor.executemany("INSERT INTO visits VALUES (?,?)", [list(d.values()) for d in user_list]) cursor.executemany("INSERT INTO types VALUES (?,?)", [list(d.values()) for d in types]) conn.commit() cursor.execute(""" SELECT * FROM visits WHERE name IN (SELECT visits.name FROM visits INNER JOIN types ON visits.date=types.date GROUP BY visits.name HAVING count(visits.name) >1) """) print(cursor.fetchall()) cursor.close() Выдает: [('maxim', '200118'), ('ivan', '110217'), ('ivan', '170518'), ('maxim', '110217')]

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

Отправить комментарий