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

Time:03-29

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.

CodePudding user response:

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.

CodePudding user response:

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.

CodePudding user response:


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 :)

CodePudding user response:

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.)

CodePudding user response:

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'}]

CodePudding user response:

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']) )]
  • Related