Объединение нескольких словарей с несовместимыми ключами - PullRequest
0 голосов
/ 01 марта 2019

Я новичок в Python и борюсь со следующим:

Я пытаюсь объединить несколько списков с вложенными словарями, которые я декодировал из нескольких jsons.Общим потоком между списками является ключ «uid» для каждого вложенного dict, соответствующего имени, но проблема в том, что некоторые dicts имеют разные имена для ключей.Например, вместо «uid» в качестве ключа у dict может быть «число».Я хотел бы объединить их части в супер-вложенный словарь.Чтобы проиллюстрировать, что у меня есть:

masterlist = [ ]

listA = [{"uid": "12345", "name": "John Smith"}, {etc...}]

listB = [{"number": "12345", "person": "John Smith", "val1": "25"}, {etc...}]

listC = [{"number": "12345", "person": "John Smith", "val2": "65"}, {etc...}]

Что я хотел бы получить в итоге:

masterlist = [{"uid": "12345", "name": "John Smith", "val1": "25", "val2: "65"}, {etc...}]

Возможно ли это сделать эффективно / pythonically путем итерации и сравнениядля идентичного значения "UID"?Я видел много инструкций по слиянию при сопоставлении ключей, но проблема здесь, очевидно, заключается в том, что ключи не согласованы.Сортировка не имеет значения.Все, что мне нужно, это чтобы основной список содержал соответствующий uid, имя и значения для каждой записи в dict.Надеюсь, это имеет смысл, и спасибо!

Ответы [ 4 ]

0 голосов
/ 09 июня 2019

Вы можете сделать это без панд, используя понимание списка, которое создает словарь словарей для группировки словарей списка по их «uid».Затем вы берете .values ​​() этого словаря группировки, чтобы снова получить список словарей:

listA = [{"uid": "12345", "name": "John Smith"},{"uid": "67890", "name": "Jane Doe"}]

listB = [{"number": "12345", "person": "John Smith", "val1": "25"},{"number": "67890", "val1": "37"}]

listC = [{"number": "12345", "person": "John Smith", "val2": "65"},{"number": "67890", "val2": "53"}]

from collections import defaultdict
fn     = { "number":"uid", "person":"name" } # map to get uniform key names
data   = [ { fn.get(k,k):v for k,v in d.items() } for d in listA+listB+listC ]
result = next(r for r in [defaultdict(dict)] if [r[d["uid"]].update(d) for d in data])
print(*result.values())

{'uid': '12345', 'name': 'John Smith', 'val1': '25', 'val2': '65'} 
{'uid': '67890', 'name': 'Jane Doe', 'val1': '37', 'val2': '53'}
0 голосов
/ 01 марта 2019

Возможно, есть решения, использующие базовый питон, но самый простой способ, который я могу придумать, это использовать библиотеку pandas для преобразования каждого списка в DataFrame, а затем объединить / объединить их вместе.

import pandas as pd

dfA = pd.DataFrame(listA)
dfB = pd.DataFrame(listB)

merged_df = dfA.merge(dfB, left_on='uid', right_on='number')

Это было бывернуть DataFrame с большим количеством столбцов, чем вам нужно (то есть будут столбцы как для «uid», так и для «number»), но вы можете указать, какие из них вы хотите, и порядок, в котором вы хотите их, следующим образом:

merged_df = merged_df[['uid', 'name', 'val1']]

Чтобы объединить несколько фреймов данных в один главный фрейм, см. Здесь: трехстороннее объединение панд в несколько фреймов данных в столбцах

0 голосов
/ 01 марта 2019

Если вам нужно использовать разные ключи для каждого списка, вот решение, которое также использует промежуточное значение dict, с функцией, которая принимает клавишу, представляющую uid, и одну или несколько клавиш для копирования:

people_by_uid = {person["uid"]: person for person in listA}

def update_values(listX, uid_key, *val_keys):
    for entry in listX:
        person = people_by_uid[entry[uid_key]]
        for val_key in val_keys:
            person[val_key] = entry[val_key]

update_values(listB, "number", "val1")
update_values(listC, "number", "val2")

# e.g. if you had a listD from which you also needed val3 and val4:
update_values(listD, "number", "val3", "val4")

masterlist = [person for person in people_by_uid.values()]
0 голосов
/ 01 марта 2019

Вы должны поместить все свои входные списки в список списков, чтобы можно было создать dict, который отображает uid на dict с агрегированными значениями элементов, чтобы желаемый список dict был просто значениями dictкартированиеЧтобы разрешить непоследовательное именование клавиши в разных входных диктовках, pop те, которые вам не нужны (например, number и id в моем примере), и присвойте диктовке ключ, который вы хотите сохранить (например, uid в примере):

wanted_key = 'uid'
unwanted_keys = {'number', 'id'}
mapping = {}
for l in lists:
    for d in l:
        if wanted_key not in d:
            d[wanted_key] = d.pop(unwanted_keys.intersection(d).pop())
        mapping.setdefault(d[wanted_key], {}).update(d)
masterlist = list(mapping.values())

, поэтому задано:

lists = [
    [
        {"uid": "12345", "name": "John Smith"},
        {"uid": "56789", "name": "Joe Brown", "val1": "1"}
    ],
    [
        {"number": "12345", "name": "John Smith", "val1": "25"},
        {"number": "56789", "name": "Joe Brown", "val2": "2"}
    ],
    [
        {"id": "12345", "name": "John Smith", "val2": "65"}
    ]
]

masterlist становится:

[
    {'uid': '12345', 'name': 'John Smith', 'val1': '25', 'val2': '65'},
    {'uid': '56789', 'name': 'Joe Brown', 'val1': '1', 'val2': '2'}
]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...