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