How to write two else condition in dict comprehension - python

2 dictionary d1,d2, create a new dictionary with same keys.
d1 = {'product': '8', 'order': '8', 'tracking': '3'}
d2 = {'order': 1, 'product': 1,'customer':'5'}
dict3 = { k: [ d1[k], d2[k] ] if k in d2 else [d1[k]] for k in d1 }
dict3
{'product': ['8', 1], 'order': ['8', 1], 'tracking': ['3']}
How to pass else [d2[k]] for k in d2 to get the expected out
My Expected out
{'product': ['8', 1], 'order': ['8', 1], 'tracking': ['3'],'customer':['5']}
Disclaimer. I have done with defaultdict. Please give answer in dict comprehension only

You could use a nested ternary ... if ... else (... if ... else ...), but what if there are three dictionaries, or four?
Better use a nested list comprehension and iterate over the different dictionaries.
>>> d1 = {'product': '8', 'order': '8', 'tracking': '3'}
>>> d2 = {'order': 1, 'product': 1,'customer':'5'}
>>> {k: [d[k] for d in (d1, d2) if k in d] for k in set(d1) | set(d2)}
{'customer': ['5'], 'order': ['8', 1], 'product': ['8', 1], 'tracking': ['3']}

You have to iterate over both the dictionaries to include all the keys in new constructed dict.
You can achieve this by using defaultdict
from collections import defaultdict
res = defaultdict(list)
for key, value in d1.items():
res[key].append(value)
for key, value in d2.items():
res[key].append(value)
Output:
>>> dict(res)
>>> {'product': ['8', 1], 'order': ['8', 1], 'tracking': ['3'], 'customer': ['5']}

Using a defaultdict without a comprehension is a much, much better way to go, but as requested:
d1 = {'product': '8', 'order': '8', 'tracking': '3'}
d2 = {'order': 1, 'product': 1,'customer':'5'}
d3 = {
k: [d1[k], d2[k]]
if (k in d1 and k in d2)
else [d1[k]]
if k in d1
else [d2[k]]
for k in list(d1.keys()) + list(d2.keys())
}
d3 is now:
{'product': ['8', 1], 'order': ['8', 1], 'tracking': ['3'], 'customer': ['5']}

>>> d1 = {'product': '8', 'order': '8', 'tracking': '3'}
>>> d2 = {'order': 1, 'product': 1, 'customer': '5'}
>>> dict3 = {k: [d1[k], d2[k]] if k in d1 and k in d2 else [d1[k]] if k in d1 else [d2[k]] for list in [d1, d2] for k in list}
>>> dict3
{'product': ['8', 1], 'order': ['8', 1], 'tracking': ['3'],'customer':['5']}

d1 = {'product': '8', 'order': '8', 'tracking': '3'}
d2 = {'order': 1, 'product': 1, 'customer': '5'}
list_ = []
for i in [d1, d2]:
list_.append(i)
list_
[{'product': '8', 'order': '8', 'tracking': '3'},
{'order': 1, 'product': 1, 'customer': '5'}]
dict_={}
for d in list_:
for k,v in d.items():
dict_.setdefault(k,[]).append(v)
dict_
{'product': ['8', 1], 'order': ['8', 1], 'tracking': ['3'], 'customer': ['5']}
Comprehension
combined_key = {key for d in list_ for key in d}
combined_key
{'customer', 'order', 'product', 'tracking'}
super_dict = {key:[d[key] for d in list_ if key in d] for key in combined_key}
super_dict
{'customer': ['5'], 'tracking': ['3'], 'order': ['8', 1], 'product': ['8', 1]}

Related

Combining multiple nested dictionaries in python

I have multiple nested dictionaries with different levels and I would like to combine them on the same key. Here, I am sharing with 3 examples such as:
dict_1={'D': {'D': '1','B': '2','A': '3'},'A': {'A': '5','J': '6'}}
dict_2={'D': {'D': '7', 'B': '8', 'C': '9'},'A': {'A': '12', 'C':'13'}}
dict_3={'D': {'test1': '14','test2': '3'},'B': {'test1': '21','test2': '16'},'A': {'test1': '3','test2': '2'},'J': {'test1': '15','test2': '3'}, 'C':{'test1': '44','test2': '33'}}
I want to combine these 3 as by adding 'dict_3' keys (adding the information from dict_3) and values to the combination of 'dict_1' and 'dict_2' for each key:
main_dict={
'D':
{'D':{'dict_1_value':'1', 'dict_2_value':'7', 'test1': '14', 'test2': '3'},
'B':{'dict_1_value':'2', 'dict_2_value':'8', 'test1': '21', 'test2': '16'},
'A':{'dict_1_value':'3', 'test1': '3', 'test2': '2'},
'C':{'dict_2_value':'9', 'test1': '44', 'test2': '33'}},
'A':
{'A':{'dict_1_value':'5', 'dict_2_value':'12', 'test1': '3', 'test2': '2'},
'J':{'dict_1_value':'6', 'test1': '15', 'test2': '3'},
'C':{'dict_2_value':'13', 'test1': '44', 'test2': '33'}}
}
At first, I have tried to combine dict_1 and dict_2 but I am overwriting the values from dict_1 when I tried such as {k: v | dict_2[k] for k, v in dict_1.items()} or dict(**dict_1,**dict_2). Moreover, I don't know how I can add dict_3 by adding key name as 'dict_1_value' or 'dict_2_value'.
Is there any way to accomplish main_dict?
all_keys = set(dict_1.keys()).union(dict_2.keys())
temp_1 = {key: {k: {'dict_1_value': v} for k, v in sub.items()} for key, sub in dict_1.items()}
temp_2 = {key: {k: {'dict_2_value': v} for k, v in sub.items()} for key, sub in dict_2.items()}
combined = {}
for key in all_keys:
sub_1 = temp_1.get(key, {})
sub_2 = temp_2.get(key, {})
sub_keys = set(sub_1.keys()).union(sub_2.keys())
combined[key] = {k: sub_1.get(k, {}) | sub_2.get(k, {}) for k in sub_keys}
Now there are 2 options:
1
Dictionary comprehension - the new dictionary is constructed from scratch:
main_dict = {key: {k: sub[k] | dict_3.get(k, {})
for k, v in sub.items()}
for key, sub in combined.items()}
2
Loop - items of the existing dictionary are just updated:
for key, sub in combined.items():
for k, v in sub.items():
v.update(dict_3.get(k, {}))
main_dict = combined

dict from a dict of list

I have a Python dictionary with following format:
d1 = {'Name':['ABC'], 'Number':['123'], 'Element 1':['1', '2', '3'],
'Element2':['1','2','3']}
Expected output:
{'Name': 'ABC', 'Number': '123',
'Elements': [{'Element 1': '1', 'Element2': '1'},
{'Element 1': '2', 'Element2': '2'},
{'Element 1': '3', 'Element2': '3'}]
I have tried the following:
[{k: v[i] for k, v in d1.items() if i < len(v)}
for i in range(max([len(l) for l in d1.values()]))]
but getting this result:
[{'Name': 'ABC', 'Number': '123', 'Element 1': '1', 'Element 2': '1'},
{'Element 1': '2', 'Element 2': '2'},
{'Element 1': '3', 'Element 2': '3'}]
How can I go from here?
I strongly recommend not trying to do everything in one line. It's not always more efficient, and almost always less readable if you have any branching logic or nested loops.
Given your dict, we can pop() the Name and Number keys into our new dict. Then
output = dict()
d1 = {'Name':['ABC'], 'Number':['123'], 'Element 1':['1', '2', '3'], 'Element2':['1','2','3']}
output["Name"] = d1.pop("Name")
output["Number"] = d1.pop("Number")
print(output)
# prints:
# {'Name': ['ABC'], 'Number': ['123']}
print(d1)
# prints:
# {'Element 1': ['1', '2', '3'], 'Element2': ['1', '2', '3']}
Then, we zip all remaining values in the dictionary, and add them to a new list:
mylist = []
keys = d1.keys()
for vals in zip(*d1.values()):
temp_obj = dict(zip(keys, vals))
mylist.append(temp_obj)
print(mylist)
# prints:
# [{'Element 1': '1', 'Element2': '1'},
# {'Element 1': '2', 'Element2': '2'},
# {'Element 1': '3', 'Element2': '3'}]
And finally, assign that to output["Elements"]
output["Elements"] = mylist
print(output)
# prints:
# {'Name': ['ABC'], 'Number': ['123'], 'Elements': [{'Element 1': '1', 'Element2': '1'}, {'Element 1': '2', 'Element2': '2'}, {'Element 1': '3', 'Element2': '3'}]}
Since you don't want to hardcode the first two keys,
for k, v in d1.items():
if "element" not in k.lower():
output[k] = v
Or as a dict-comprehension:
output = {k: v for k, v in d1.items() if "element" not in k.lower()}
use a list of tuples to create the elements list of dictionaries. Use Convert to build your dictionary item from the tuple.
#https://www.geeksforgeeks.org/python-convert-list-tuples-dictionary/
d1 = {'Name':['ABC'], 'Number':['123'], 'Element 1':['1', '2', '3'],
'Element2':['1','2','3']}
def Convert(tup, di):
for a, b in tup:
di[a]=b
return di
dict={}
listElements=[]
for key,value in d1.items():
if isinstance(value,list) and len(value)>1:
for item in value:
listElements.append((key,item))
elif isinstance(value,list) and len(value)==1:
dict[key]=value[0]
else:
dict[key]=value
dict['Elements']=[Convert([(x,y)],{}) for x,y in listElements]
print(dict)
output:
{'Name': 'ABC', 'Number': '123', 'Elements': [{'Element 1': '1'}, {'Element 1': '2'}, {'Element 1': '3'}, {'Element2': '1'}, {'Element2': '2'}, {'Element2': '3'}]}
I'm going to explain step by step:
We build new_d1 variable, that is the dictionary you expect as output and it's initialized as {'Name': 'ABC', 'Number': '123'}. For achieving the above, we use comprehension notation taking into account the keys != 'Element'
new_d1 = {key: d1.get(key)[0] for key in filter(lambda x: 'Element' not in x, d1)}
We build elements variable, that's a list with the dictionaries matter for us, I mean, the dictionaries we have to manipulate to achieve the expected result. Then elements is [{'Element 1': ['1', '2', '3']}, {'Element2': ['1', '2', '3']}].
elements = [{key: d1.get(key)} for key in filter(lambda x: 'Element' in x, d1)]
We are going to do a Cartesian product using itertools.product taking into account each key and each item of the values present in elements.
product = [list(it.product(d.keys(), *d.values())) for d in elements]
Using zip, we arrange the data and covert them in dictionary. And finally we create "Elements" key in new_df1
elements_list = [dict(t) for index, t in enumerate(list(zip(*product)))]
new_d1["Elements"] = elements_list
print(new_d1)
Full code:
import itertools as it
new_d1 = {key: d1.get(key)[0] for key in filter(lambda x: 'Element' not in x, d1)}
elements = [{key: d1.get(key)} for key in filter(lambda x: 'Element' in x, d1)]
product = [list(it.product(d.keys(), *d.values())) for d in elements]
elements_list = [dict(t) for index, t in enumerate(list(zip(*product)))]
new_d1["Elements"] = elements_list
Output:
{'Elements': [{'Element 1': '1', 'Element2': '1'},
{'Element 1': '2', 'Element2': '2'},
{'Element 1': '3', 'Element2': '3'}],
'Name': 'ABC',
'Number': '123'}

How to merge dict of dict in python

Two dictionary is below
d1 = {'1': {'index': '1', 'sc': '4', 'st': '3'}, '2': {'index': '2', 'sc': '5', 'st': '5'}}
d2 = {'1': {'diff': 1}, '2': {'diff': 0}}
Code is below
z = {**d2, **d1} why this is not working
Tried below code also
def Merge(d1, d2):
return(d2.update(d1))
print(Merge(d1, d2))
Expetec out below
{'1': {'index': '1', 'sc': '4', 'st': '3', 'diff': 1},
'2': {'index': '2', 'sc': '5', 'st': '5', 'diff': 0}}
an alternate way using pandas
>>> import pandas as pd
>>> df = pd.DataFrame(d1)
>>> df2 = pd.DataFrame(d2)
>>> merged_dict = pd.concat([df,df2]).to_dict()
output
>>> merged_dict
{'1': {'index': '1', 'sc': '4', 'st': '3', 'diff': 1}, '2': {'index': '2', 'sc': '5', 'st': '5', 'diff': 0}}
generally, ** will capture any keyword arguments we pass to the function into a dictionary which that attributes arguments will reference. For example:
d1={'a':1,'b':2}
d2={'c':3,'d':4}
def merge(**di):
res = {}
for k, v in di.items():
try:
res[k].append(v)
except KeyError:
res[k] = [v]
return res
print(merge(**d1, **d2))
# {'a': [1], 'b': [2], 'c': [3], 'd': [4]}
However, if we pass in two dictionary with same keys:
d1 = {'1': {'index': '1', 'sc': '4', 'st': '3'}, '2': {'index': '2', 'sc': '5', 'st': '5'}}
d2 = {'1': {'diff': 1}, '2': {'diff': 0}}
def merge(**di):
res = {}
for k, v in di.items():
try:
res[k].append(v)
except KeyError:
res[k] = [v]
return res
print(merge(**d1, **d2))
# TypeError: merge() got multiple values for keyword argument '1'
This error is handled by continuing which keep the original one and skip the second dict key. Sorry I don't have a shorthand method for this.
d1 = {'1': {'index': '1', 'sc': '4', 'st': '3'}, '2': {'index': '2', 'sc': '5', 'st': '5'}}
d2 = {'1': {'diff': 1}, '2': {'diff': 0}}
def merge(*args):
res = {}
for di in args:
for k, v in di.items():
try:
res[k].update(v)
except KeyError:
res[k] = v
return res
print(merge(d1, d2))
# {'1': {'index': '1', 'sc': '4', 'st': '3', 'diff': 1}, '2': {'index': '2', 'sc': '5', 'st': '5', 'diff': 0}}
z = {**d2, **d1}
Overwrites everything in d2 with d1 values for keys '1', and '2'. It is tricky to merge dictionaries with the same keys so you don't overwrite key:value pairs within those keys.
The following will get you to the depth needed in both d1 and d2 to update d1 to your expected output:
d1['1']['diff'] = d2['1']['diff']
d1['2']['diff'] = d2['2']['diff']
print ('d1:', d1)
Output:
d1: {'1': {'index': '1', 'sc': '4', 'st': '3', 'diff': 1}, '2': {'index': '2', 'sc': '5', 'st': '5', 'diff': 0}}
>>> for key in d1:
... d1[key].update(d2[key])
>>> d1
{'1': {'index': '1', 'sc': '4', 'st': '3', 'diff': 1}, '2': {'index': '2', 'sc': '5', 'st': '5', 'diff': 0}}
Update:
If you want in another identifier d3.
d3 = d1.copy()
for key in d3:
d3[key].update(d2[key])
print(d3)
Dictionaries are mutable objects. update function just mutates/updates the object and return None. So, you need to create a copy (so you have another object) and change the new object, if you want initial data unaltered.

How to subtract the values inside a dictionary

I have dictionary below
d = {'1': {'index': '1', 'sc': '4', 'st': '3'},
'2': {'index': '2', 'sc': '5', 'st': '5'}}
I need to create a new key diff with subtract sc - st
expected out
d1 = {'1': {'index': '1', 'sc': '4', 'st': '3', 'diff': 1},
'2': {'index': '2', 'sc': '5', 'st': '5', 'diff': 0}}
d2 = {'1':{'diff': 1}, '2': {'diff': 0}}
Pseudo code
from functools import reduce
d1 = {}
d2 = {}
for i,j in m.items():
#print (j)
for a,b in j.items():
#print (b)
d1['diff'] = reduce(lambda x,y: x-y, [int(i) for i in d.values()])
You can try
d1 = {k: {**v, "diff": int(v['sc']) - int(v['st'])} for k, v in d.items()}
d2 = {k: {"diff": int(v['sc']) - int(v['st'])} for k, v in d.items()}
Result
d1 = {'1': {'index': '1', 'sc': '4', 'st': '3', 'diff': 1}, '2': {'index': '2', 'sc': '5', 'st': '5', 'diff': 0}}
d2 = {'1': {'diff': 1}, '2': {'diff': 0}}

Exclude repeated values from a dictionary and increment the 'qty' field accordingly

Considering '1', '2', '3', '4' are the indexes and everything else as the values of a dictionary in Python, I'm trying to exclude the repeating values and increment the quantity field when a dupicate is found. e.g.:
Turn this:
a = {'1': {'name': 'Blue', 'qty': '1', 'sub': ['sky', 'ethernet cable']},
'2': {'name': 'Blue', 'qty': '1', 'sub': ['sky', 'ethernet cable']},
'3': {'name': 'Green', 'qty': '1', 'sub': []},
'4': {'name': 'Blue', 'qty': '1', 'sub': ['sea']}}
into this:
b = {'1': {'name': 'Blue', 'qty': '2', 'sub': ['sky', 'ethernet cable']},
'2': {'name': 'Green', 'qty': '1', 'sub': []},
'3': {'name': 'Blue', 'qty': '1', 'sub': ['sea']}}
I was able to exclude the duplicates, but I'm having a hard time incrementing the 'qty' field:
b = {}
for k,v in a.iteritems():
if v not in b.values():
b[k] = v
P.S.: I posted this question earlier, but forgot to add that the dictionary can have that 'sub' field which is a list. Also, don't mind the weird string indexes.
First, convert the original dict 'name' and 'sub' keys to a comma-delimited string, so we can use set():
data = [','.join([v['name']]+v['sub']) for v in a.values()]
This returns
['Blue,sky,ethernet cable', 'Green', 'Blue,sky,ethernet cable', 'Blue,sea']
Then use the nested dict and list comprehensions as below:
b = {str(i+1): {'name': j.split(',')[0], 'qty': sum([int(qty['qty']) for qty in a.values() if (qty['name']==j.split(',')[0]) and (qty['sub']==j.split(',')[1:])]), 'sub': j.split(',')[1:]} for i, j in enumerate(set(data))}
Maybe you can try to use a counter like this:
b = {}
count = 1
for v in a.values():
if v not in b.values():
b[str(count)] = v
count += 1
print b

Categories

Resources