Asked  7 Months ago    Answers:  5   Viewed   24 times

I need to merge multiple dictionaries, here's what I have for instance:

dict1 = {1:{"a":{A}}, 2:{"b":{B}}}

dict2 = {2:{"c":{C}}, 3:{"d":{D}}

With A B C and D being leaves of the tree, like {"info1":"value", "info2":"value2"}

There is an unknown level(depth) of dictionaries, it could be {2:{"c":{"z":{"y":{C}}}}}

In my case it represents a directory/files structure with nodes being docs and leaves being files.

I want to merge them to obtain:

 dict3 = {1:{"a":{A}}, 2:{"b":{B},"c":{C}}, 3:{"d":{D}}}

I'm not sure how I could do that easily with Python.



this is actually quite tricky - particularly if you want a useful error message when things are inconsistent, while correctly accepting duplicate but consistent entries (something no other answer here does....)

assuming you don't have huge numbers of entries a recursive function is easiest:

def merge(a, b, path=None):
    "merges b into a"
    if path is None: path = []
    for key in b:
        if key in a:
            if isinstance(a[key], dict) and isinstance(b[key], dict):
                merge(a[key], b[key], path + [str(key)])
            elif a[key] == b[key]:
                pass # same leaf value
                raise Exception('Conflict at %s' % '.'.join(path + [str(key)]))
            a[key] = b[key]
    return a

# works
print(merge({1:{"a":"A"},2:{"b":"B"}}, {2:{"c":"C"},3:{"d":"D"}}))
# has conflict
merge({1:{"a":"A"},2:{"b":"B"}}, {1:{"a":"A"},2:{"b":"C"}})

note that this mutates a - the contents of b are added to a (which is also returned). if you want to keep a you could call it like merge(dict(a), b).

agf pointed out (below) that you may have more than two dicts, in which case you can use:

reduce(merge, [dict1, dict2, dict3...])

where everything will be added to dict1.

[note - i edited my initial answer to mutate the first argument; that makes the "reduce" easier to explain]

ps in python 3, you will also need from functools import reduce

Tuesday, June 1, 2021
answered 7 Months ago

Take your pick:

hs.reduce({}) {|h,pairs| pairs.each {|k,v| (h[k] ||= []) << v}; h}{}) {|h,(k,v)| (h[k] ||= []) << v; h}

I'm strongly against messing with the defaults for hashes, as the other suggestions do, because then checking for a value modifies the hash, which seems very wrong to me.

Tuesday, July 27, 2021
answered 5 Months ago

Just switch the order:

z = dict(d2.items() + d1.items())

By the way, you may also be interested in the potentially faster update method.

In Python 3, you have to cast the view objects to lists first:

z = dict(list(d2.items()) + list(d1.items())) 

If you want to special-case empty strings, you can do the following:

def mergeDictsOverwriteEmpty(d1, d2):
    res = d2.copy()
    for k,v in d2.items():
        if k not in d1 or d1[k] == '':
            res[k] = v
    return res
Monday, August 2, 2021
answered 5 Months ago

Plese find the below code for your query.

result ={}
final_op = {}
for k,v in dictA.iteritems():
    for nk,nv in v.iteritems():
        if result.has_key(nk):
            while i < len(result[nk]):
                result[nk][i] += nv[i]
                i += 1
            result[nk] = nv
final_op['nest'] = result
print final_op


{'nest': {'02feb': [7, 11, 16, 19, 20], '01feb': [2, 4, 6, 8, 10]}}
Thursday, August 19, 2021
answered 4 Months ago

In general, I would say it's bad practice to cast the values of different keys as different object types. I would simply do something like:

def merge_values(val1, val2):
    if val1 is None:
        return [val2]
    elif val2 is None:
        return [val1]
        return [val1, val2]
dict3 = {
    key: merge_values(dic1.get(key), dic2.get(key))
    for key in set(dic1).union(dic2)
Tuesday, September 21, 2021
Diego Rafael Souza
answered 3 Months ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :