简体   繁体   中英

Most Pythonic way to merge two dictionnaries having common key/value pair

I have two lists of python dictionnaries:

l1 = [{"id":1, "name":"A"}, {"id":2, "name":"B"}]
l2 = [{"id":1, "full_name":"Alfred"}, {"id":2, "full_name":"Barbara"}]

My goal is to have a list as follow:

l3 = [{"id":1, "full_name":"Alfred", "name":"A"}, {"id":2, "full_name":"Barbara", "name":"B"}]

I think I could use nested loops to do this, but I'm sure it should be possible to do it in a better and more pythonic way.

from collections import defaultdict


res = defaultdict(dict)
for a, b in zip(l1, l2):
    key1 = a['id']
    key2 = b['id']
    res[key1].update(a)
    res[key2].update(b)

print(list(res.values())) # [{'id': 1, 'name': 'A', 'full_name': 'Alfred'}, {'id': 2, 'name': 'B', 'full_name': 'Barbara'}]

Although this would work in a single iteration you might want to simplify your underlying data structures a bit to make them easier to work with.

In one line (assuming id fields have perfect 1:1 mapping):

result = [x | y for x, y in zip(sorted(l1, key = lambda x: x['id']), sorted(l2, key = lambda x: x['id']) )]

Assuming you sort the lists by the ID value in advance and that the ID values in the two lists are the same (just possibly in a different order), we can do it with a simple zip call.

l1.sort(key=lambda x: x["id"])
l2.sort(key=lambda x: x["id"])

def merge(x, y):
    a = {}
    a.update(x)
    a.update(y)
    return a

l3 = [merge(x, y) for x, y in zip(l1, l2)]

Note that in Python 3.9 and newer, merge is simply | , so we can write

l3 = [x | y for x, y in zip(l1, l2)]

and eliminate the need for the helper function altogether.

I'd suggest restructuring your input data when creating it, so that id is an actual identifier for each specific person. If you make value of id key for an outer dictionary, you can do it in a single pass through one each of the mappings.

from collections import defaultdict
l1 = {1:{"name":"A"}, 2:{"name":"B"}, 3:{"name":"C"}}
l2 = {1:{"full_name":"Alfred"}, 2:{"full_name":"Barbara"}}

result = defaultdict(dict)

for li in [l1, l2]:
    for k,v in li.items():
        result[k].update(v)

print(result)

(I removed the "id":value from the data because it's redundant with this form of data but adding it back in should be no problem if you really need it there.)


l1 = [{"id":1, "name":"A"}, {"id":2, "name":"B"}]
l2 = [{"id":1, "full_name":"Alfred"}, {"id":2, "full_name":"Barbara"}]


print([{**i,**j} for i in l1 for j in l2 if i["id"] ==j["id"]])

Result:

[{'id': 1, 'name': 'A', 'full_name': 'Alfred'}, {'id': 2, 'name': 'B', 'full_name': 'Barbara'}]

you could try something like this, which you maybe already knew... would be interesting to see something more pythonic:)

Use the id as identifer, create new dicts and update them - more comments in code.

No sorting needed, un"even" source lists inner dicts will be kept as well

l1 = [{"id":1, "name":"A"}, {"id":2, "name":"B"}]
l2 = [{"id":1, "full_name":"Alfred"}, {"id":2, "full_name":"Barbara"}]

# intermediate dictionary that keeps all collected inner dicts at the id
d = {}
# go through both lists
for inner_d in l1 + l2:
    # create a new empty dict under the id if needed 
    what = d.setdefault(inner_d["id"], {})
    # add all things from the inner dict
    what.update(inner_d.items())

as_list = list(d.values())
print(as_list)

Output:

[{'id': 1, 'name': 'A', 'full_name': 'Alfred'}, 
 {'id': 2, 'name': 'B', 'full_name': 'Barbara'}]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM