Более быстрый способ объединения словарей с одинаковым идентификатором

#python

#python

Вопрос:

Допустим, у меня есть следующие списки словарей с одинаковым идентификатором атрибута. Я хочу знать, какой самый быстрый и правильный способ объединить их в соответствии со значением идентификатора.

  perperson = [
  {'id':1, 'firstName':'test','lastName':'testlast'},
  {'id':2, 'firstName':'test2','lastName':'testlast2'},
  {'id':3, 'firstName':'test3','lastName':'last3'},
]

peremail = [
  {'id':1, 'email':'test@test'},
  {'id':2, 'email':'test2@test2'},
  {'id':3, 'email':'test3@test3'},
]
  

Результат

  comdined= [
  {'id':1, 'firstName':'test','lastName':'testlast','email':'test@test'},
  {'id':2, 'firstName':'test2','lastName':'testlast2','email':'test2@test2'},
  {'id':3, 'firstName':'test3','lastName':'last3','email':'test3@test3'},
]
  

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

1. comdined= [{**x, **y} for x,y in zip(perperson, peremail)]

2. Списки уже отсортированы по идентификатору? Существует ли один и тот же набор идентификаторов в обоих списках?

3. Преобразуйте свой список в список dicts по идентификаторам… Звучит более уместно

4. В вашем названии говорится более быстрый способ — быстрее, чем что? Вы не показали свой текущий код (если он есть …)

Ответ №1:

Преобразуйте один из списка в dict, а затем выполните поиск

Пример:

 perperson = [
  {'id':1, 'firstName':'test','lastName':'testlast'},
  {'id':2, 'firstName':'test2','lastName':'testlast2'},
  {'id':3, 'firstName':'test3','lastName':'last3'},
]

peremail = [
  {'id':1, 'email':'test@test'},
  {'id':2, 'email':'test2@test2'},
  {'id':3, 'email':'test3@test3'},
]
peremail_t = {i.pop('id'): i for i in peremail}    # Easy look-up

comdined = [{**i, **peremail_t[i['id']]} for i in perperson]
print(comdined)
  

Вывод:

 [{'email': 'test@test', 'firstName': 'test', 'id': 1, 'lastName': 'testlast'},
 {'email': 'test2@test2',
  'firstName': 'test2',
  'id': 2,
  'lastName': 'testlast2'},
 {'email': 'test3@test3', 'firstName': 'test3', 'id': 3, 'lastName': 'last3'}]
  

ИЛИ обновить на месте

Пример:

 for i in perperson:
    i.update(peremail_t[i['id']])
  

Ответ №2:

Если вы имеете дело с большими объемами табличных данных в списках словарей, рассмотрите возможность использования фрейма данных Pandas. Объединение фреймов данных по идентификатору очень простое, оно будет быстрее, если таблицы будут большими, и это дает вам больше возможностей для решения потенциальных проблем, таких как несоответствие идентификаторов.

 import pandas as pd
merged = pd.DataFrame(perperson).merge(pd.DataFrame(peremail), on="id")
  

Вы можете использовать merged.to_dict("records") , если вам нужно преобразовать его обратно в словари.

Если вы не хотите использовать pandas, вот генератор, который может объединять произвольное количество списков словарей, которые могут быть не отсортированы и могут иметь несовпадающие идентификаторы (что эквивалентно «внешнему» слиянию в pandas). Это, вероятно, будет медленнее, чем преобразование списков в словари, но максимально эффективно с использованием списков.

 def join_by_key(key, *lists):
    lists = [sorted(L, key=lambda d: d[key]) for L in lists]
    while lists:
        min_key = min(L[0][key] for L in lists)
        r = {}
        for L in lists:
            if L[0][key] == min_key:
                r.update(L.pop(0))
        yield r
        lists = [L for L in lists if L]
            
print(list(join_by_key("id", perperson, peremail)))
  

Ответ №3:

вот мое предложение простого цикла:

 perperson = [{'id':1, 'firstName':'test','lastName':'testlast'},
{'id':2, 'firstName':'test2','lastName':'testlast2'},
{'id':3, 'firstName':'test3','lastName':'last3'},
]

peremail = [
{'id':1, 'email':'test@test'},
{'id':2, 'email':'test2@test2'},
{'id':3, 'email':'test3@test3'},
]


for n,j in zip(perperson,peremail):
    n['email']=j['email']

print(perperson)
  

ее вывод

 [{'lastName': 'testlast', 'id': 1, 'firstName': 'test', 'email': 'test@test'},      {'lastName': 'testlast2', 'id': 2, 'firstName': 'test2', 'email': 'test2@test2'}, {'lastName': 'last3', 'id': 3, 'firstName': 'test3', 'email': 'test3@test3'}]
  

Ответ №4:

Учитывая, что все словари имеют ключ «id», а списки упорядочены по значениям «id»:

 
def combine_dicts(dict_1, dict_2):
    if dict_1['id'] == dict_2['id']:
        for k in dict_2:
            if k in dict_1:
                continue
            else:
                dict_1.update({k:dict_2[k]})
    return dict_1


for dict1, dict2 in zip(perperson, peremail):
    combine_dicts(dict1, dict2)