Rahul Sharma
Rahul Sharma

Reputation: 2495

Combine two or more dictionaries with same keys

I have two dictionaries items and u_items

items = {"A": 1, "B": 2, "C": 3}

u_items = {"D": 4, "B": 4, "E": 8, "C": 4}

I want to update the items dictionary with u_items so I did this

items.update((k + '_1' if k in items else k, v) for k, v in u_items.items())

such that I can differentiate keys from both dictionaries

Output:

items = {'A': 1, 'B': 2, 'C': 3, 'D': 4, 'B_1': 4, 'E': 8, 'C_1': 4}

but when i update the items dictionary with another dictionary, let's say n_items, it replaces the value of B_1 instead of making it B_1_1

n_items = {"C":7, "B":9}

items.update((k + '_1' if k in items else k, v) for k, v in n_items.items())

output:

{'A': 1, 'B': 2, 'C': 3, 'D': 4, 'B_1': 9, 'E': 8, 'C_1': 7}

But I want the output to be like this:

{'A': 1, 'B': 2, 'C': 3, 'D': 4, 'B_1': 4, 'E': 8, 'C_1': 4, 'B_1_1':9,'C_1_1':7}

or like this:

{'A': 1, 'B': 2, 'C': 3, 'D': 4, 'B_1': 4, 'E': 8, 'C_1': 4, 'B_2':9,'C_2':7}

How can I do it ?

Upvotes: 5

Views: 151

Answers (7)

Subham
Subham

Reputation: 411

Since we deal with duplicates by adding a _count eg next is C_1 we only need to check the first character lambda x:x.startswith(key[0]) basically iterates through result.keys() and checks whether any share the first character so if keys has C and C_1 , we get a list of 2 once we have that , the length of that list would be the count for next C, i.e len( 'C', 'C_1') is 2, hence next we have 'C_2'

def combine(*args):
    result = {}
    for d in args:
        for key, value in d.items():
            key = str(key)
            if key in result:
                keys = filter( lambda x:x.startswith(key[0]), result.keys())
                keys = len(list(keys))
                key = f'{key}_{keys}'
            result[key] = value

    return result

items = {"A": 1, "B": 2, "C": 3}

u_items = {"D": 4, "B": 4, "E": 8, "C": 4}

n_items = {"C":7, "B":9}

print(combine(items, u_items, n_items))
{'A': 1, 'B': 2, 'C': 3, 'D': 4, 'B_1': 4, 'E': 8, 'C_1': 4, 'C_2': 7, 'B_2': 9}

[Program finished]

Upvotes: 0

CristiFati
CristiFati

Reputation: 41116

Although this seems a bit like an XY Problem, here's an ugly (and I'm pretty sure, inefficient) and also, not very general solution consisting of:

that merges the dictionaries, by appending "_1" to an existing key (doing everything in one line), as you requested, although I'd recommend (since there are cases when the shortest is not necessarily the best):

  • Using a function (to avoid duplicating code (the expression))
  • Subclassing dict, and overriding its update method (a nicer variant of the previous one)
>>> items = {"A": 1, "B": 2, "C": 3}
>>> u_items = {"D": 4, "B": 4, "E": 8, "C": 4}
>>> n_items = {"C": 7, "B": 9}
>>>
>>> items.update((max([k1 + "_1" for k1 in items if k1 == k or k1.startswith(k + "_1")], key=len, default=k), v) for k, v in u_items.items())
>>> items
{'A': 1, 'B': 2, 'C': 3, 'D': 4, 'B_1': 4, 'E': 8, 'C_1': 4}
>>>
>>> items.update((max([k1 + "_1" for k1 in items if k1 == k or k1.startswith(k + "_1")], key=len, default=k), v) for k, v in n_items.items())
>>> items
{'A': 1, 'B': 2, 'C': 3, 'D': 4, 'B_1': 4, 'E': 8, 'C_1': 4, 'C_1_1': 7, 'B_1_1': 9}
>>>
>>>
>>> # Merging an additional dictionary
...
>>> v_items = {"C": 25}
>>>
>>> items.update((max([k1 + "_1" for k1 in items if k1 == k or k1.startswith(k + "_1")], key=len, default=k), v) for k, v in v_items.items())
>>> items
{'A': 1, 'B': 2, 'C': 3, 'D': 4, 'B_1': 4, 'E': 8, 'C_1': 4, 'C_1_1': 7, 'B_1_1': 9, 'C_1_1_1': 25}

Upvotes: 1

Mohideen bin Mohammed
Mohideen bin Mohammed

Reputation: 20137

try this,

without function, without any module,

>>> for k, v in n_items.items():
...   _k = {k: v}
...   if k in items:
...      _k = {max(i for i in items if k in i)+'_1': v}
...   items.update(_k)
... 
>>> items
{'A': 1, 'D': 4, 'B_1': 4, 'B': 2, 'D_1': 5, 'X': 1, 'C_1_1': 7, 'C': 3, 'E': 8, 'C_1': 4, 'B_1_1': 9}
>>> 

Upvotes: 0

Benedict
Benedict

Reputation: 2821

The poster's approach of update and an if/else list comprehension is a really good start. I think the key thing (please excuse the pun) is to introduce a loop to find a key that's acceptable. It's python3, but one can resurrect reduce for this to make it a one-liner:

>>> import functools
>>> items = {"A": 1, "B": 2, "C": 3}
>>> u_items = {"D": 4, "B": 4, "E": 8, "C": 4}
>>> n_items = {"C":7, "B":9}
>>> items.update({functools.reduce(lambda c, n: c+n if c in items else c, ['_1']*2, k):*3 v for k, v in u_items.items()})
>>> items
{'A': 1, 'B': 2, 'C': 3, 'D': 4, 'B_1': 4, 'E': 8, 'C_1': 4}
>>> items.update({functools.reduce(lambda c,n: c+n if c in items else c, ['_1']*2, k): v for k, v in n_items.items()})
>>> items
{'A': 1, 'B': 2, 'C': 3, 'D': 4, 'B_1': 4, 'E': 8, 'C_1': 4, 'C_1_1': 7, 'B_1_1': 9}

Note - your list of progressive extensions ['_1']*2 will have to be as long as the number of lists you're combining. It's an exercise to construct a more interesting lambda if keys like C_1, C_2, ... are required.

Upvotes: 0

Mykola Zotko
Mykola Zotko

Reputation: 17794

You can use a small helper function:

d1 = {'A':1, 'B':2, 'B_1':3, 'B_1_1':4}
d2 = {'A':1, 'B':2}

def gen_key(key, dct):
    while key in dct:
        key += '_1'
    return key

d1.update((gen_key(k, d1), v) for k, v in d2.items())

print(d1)
# {'A': 1, 'B': 2, 'B_1': 3, 'B_1_1': 4, 'A_1': 1, 'B_1_1_1': 2}

Upvotes: 0

Chris
Chris

Reputation: 29742

Using operator.itemgetter:

items = {'A':1, 'B':2, 'C':3}
u_items = {'D':4, 'B':4, 'E':8, 'C':4}
n_items = {"C":7, "B":9}

def update_dict(d1, d2):
    l = list(map(itemgetter(0), d1))
    d1.update(('_'.join([k,str(l.count(k))]) if k in l else k, v) 
             for k,v in d2.items())

update_dict(items, u_items)
update_dict(items, n_items)

Output at first update using u_items:

{'A': 1, 'B': 2, 'B_1': 4, 'C': 3, 'C_1': 4, 'D': 4, 'E': 8}

Output at second update using n_items:

{'A': 1,
 'B': 2,
 'B_1': 4,
 'B_2': 9,
 'C': 3,
 'C_1': 4,
 'C_2': 7,
 'D': 4,
 'E': 8}

Upvotes: 0

gmds
gmds

Reputation: 19885

You can do this iteratively:

def combine(*args):
    result = {}
    for d in args:
        for key, value in d.items():
            key = str(key)
            while key in result:
                key += '_1'
            result[key] = value

    return result

print(combine(items, u_items, n_items))

Output:

{'A': 1,
 'B': 2,
 'C': 3,
 'D': 4,
 'B_1': 4,
 'E': 8,
 'C_1': 4,
 'C_1_1': 7,
 'B_1_1': 9}

Upvotes: 2

Related Questions