dict from a dict of list - python

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

Related

list duplicate values in a nested dictionary

i need to check for duplicate values that might occur in a dictionary. I have a dictionary in the following layout. Any advise is welcome! thanks so much
the original dictionary
dic = {'ab1': [{'ans': 'Male', 'val': '1'},
{'ans': 'Female', 'val': '2'},
{'ans': 'Other', 'val': '3'},
{'ans': 'Prefer not to answer', 'val': '3'}],
'bc1': [{'ans': 'Employed', 'val': '1'},
{'ans': 'Unemployed', 'val': '2'},
{'ans': 'Student', 'val': '3'},
{'ans': 'Retired', 'val': '4'},
{'ans': 'Part-time', 'val': '5'},
{'ans': 'Prefer not to answer', 'val': '7'}],
'bc2': [{'ans': 'Mother',
'val': '1'},
{'ans': 'Father ', 'val': '2'},
{'ans': 'Brother', 'val': '3'},
{'ans': 'Sister', 'val': '4'},
{'ans': 'Grandmother', 'val': '4'},
{'ans': 'Grandfather', 'val': '6'},
{'ans': 'Son', 'val': '7'},
{'ans': 'Daughter', 'val': '8'}]}
the expected output - a list that contains ONLY items with identical values per key - so only this
ab1: Other 3, Prefer not to answer 3
bc2: Sister 4, Grandmother 4
code I have tried it aims to reverse the dictionary first - but throws unhashable type list error i think because it treats it as a list when in fact the dict might be a tupple but i don't know how to change it
rev_dict = {}
for k, v in dic.items():
rev_dict.setdefault(v, set()).add(k)
res = set(chain.from_iterable(v for k, v in rev_dict.items()
if len(v) > 1))
You've not specified an exact output format, but since you tagged pandas, here's a pandas solution.
import pandas as pd
{k: pd.DataFrame(v)[lambda df: df['val'].duplicated(keep=False)].to_dict(orient='records') for k, v in dic.items()}
Output:
{
'ab1': [{'ans': 'Other', 'val': '3'},
{'ans': 'Prefer not to answer', 'val': '3'}],
'bc1': [],
'bc2': [{'ans': 'Sister', 'val': '4'}, {'ans': 'Grandmother', 'val': '4'}]
}
The panda's answer is certainly nicer:
lst = []
for i in dic.keys():
counts = Counter([j['val'] for j in dic[i]])
new = {j['ans']: j['val'] for j in dic[i] if counts[j['val']] > 1}
lst.append(i + ': ' + ', '.join(['{} {}'.format(i, new[i]) for i in new])) if new else None
Import itertools and try this:
list(itertools.chain(*[[(k, i['ans'],i['val']) for i in v] for k, v in dic.items()]))
Long version
import itertools
lst = []
for k,v in dic.items():
for i in v:
tup = (k, i['ans'],i['val'])
lst.append(tup)
list(itertools.chain(*lst))

Create a new list of dictionaries from a main list and a sublist

I have two lists (the main list and the sublist) containing different numbers of dictionaries and their values would be the same if their positions match.
mainLst = [{"name":"M_AAA_X", "position":"1", "value":"8"},
{"name":"M_AAA_X", "position":"2", "value":"10"},
{"name":"M_AAA_X", "position":"4", "value":"14"},
{"name":"M_AAA_X", "position":"5", "value":"16"},
{"name":"M_AAA_X", "position":"7", "value":"20"}]
subLst = [{"name":"S_AAA_X", "position":"1", "value":"8"},
{"name":"S_AAA_X", "position":"2", "value":"10"},
{"name":"S_AAA_X", "position":"3", "value":"12"},
{"name":"S_AAA_X", "position":"4", "value":"14"},
{"name":"S_AAA_X", "position":"5", "value":"16"},
{"name":"S_AAA_X", "position":"6", "value":"18"}]
I would like to create a new list by:
taking elements from the mainLst
if the mainLst lacks a certain position (ex. position: 3, 6), take elements from the subList
sort by 'position' in ascending order
The output should look like this:
newLst = [{"name":"M_AAA_X", "position":"1", "value":"8"},
{"name":"M_AAA_X", "position":"2", "value":"10"},
{"name":"S_AAA_X", "position":"3", "value":"12"},
{"name":"M_AAA_X", "position":"4", "value":"14"},
{"name":"M_AAA_X", "position":"5", "value":"16"},
{"name":"S_AAA_X", "position":"6", "value":"18"},
{"name":"M_AAA_X", "position":"7", "value":"20"}]
Could anyone help me to accomplish this? Thanks in advance!
Give this a try.
mainLst.extend([dictSub for dictSub in subLst if dictSub['position'] not in [pos['position'] for pos in mainLst]])
print(sorted(mainLst, key = lambda i: i['position']))
Basically we gather positions in sublist which is not in mainlist. Then we sort the list of dicts based on that.
output
[{'name': 'M_AAA_X', 'position': '1', 'value': '8'},
{'name': 'M_AAA_X', 'position': '2', 'value': '10'},
{'name': 'S_AAA_X', 'position': '3', 'value': '12'},
{'name': 'M_AAA_X', 'position': '4', 'value': '14'},
{'name': 'M_AAA_X', 'position': '5', 'value': '16'},
{'name': 'S_AAA_X', 'position': '6', 'value': '18'},
{'name': 'M_AAA_X', 'position': '7', 'value': '20'}]
Another solution, using itertools.groupby:
from itertools import groupby
out = []
for _, g in groupby(
sorted(mainLst + subLst, key=lambda k: int(k["position"])),
lambda k: int(k["position"]),
):
out.append(next(g))
print(out)
Prints:
[{'name': 'M_AAA_X', 'position': '1', 'value': '8'},
{'name': 'M_AAA_X', 'position': '2', 'value': '10'},
{'name': 'S_AAA_X', 'position': '3', 'value': '12'},
{'name': 'M_AAA_X', 'position': '4', 'value': '14'},
{'name': 'M_AAA_X', 'position': '5', 'value': '16'},
{'name': 'S_AAA_X', 'position': '6', 'value': '18'},
{'name': 'M_AAA_X', 'position': '7', 'value': '20'}]

How to get the keys if one of key is or '0' in dictionary

I have Dictionary is below, My product output is below. I need to create a new dictionary with two types Out_1, Out_2
product = {'Product1': {'index': '1', '1': 'Book', '2': 'Pencil', '3': 'Pen','value': '1'},
'Product2': {'index': '2', '1': 'Marker', '2': 'MYSQL', '3': 'Scale','value': '0'}}
If value inside product is 0 then extract the keys
Expected Output
Out_1 = {'Product2': {1:'Marker': '2': 'Compass', '3': 'Scale', 'value': 0}}
Out_2 = {'Product2':['Marker','Compass','Scale', '0']}
Psuedo code is below. i tried to create but not able to create as above
Out_1 = {}
Out_2 = {i:[]}
for i,j in product.items():
for a,b in j.items():
if a['value'] == 0:
Out_2.append(i)
I am getting indices error, How to get Out_1, Out_2
You can use dict comprehensions for this.
out_1 = {k: v for k, v in product.items() if v['value']=='0'}
out_2 = {k: list(v.values()) for k, v in product.items() if v['value']=='0'}
Hi do you really need this index variable? If not yes why would not you use list of dicts instead of dict of dicts. However here is what you wanted:
products = {'Product1': {'index': '1', '1': 'Book', '2': 'Pencil', '3': 'Pen','value': '1'},
'Product2': {'index': '2', '1': 'Marker', '2': 'MYSQL', '3': 'Scale','value': '0'}}
for k,product in products.items():
product.pop('index', None)
if product['value'] == '0':
products[k] = list(product.values())
print(products)
>>> {'Product1': {'1': 'Book', '2': 'Pencil', '3': 'Pen', 'value': '1'}, 'Product2': ['Marker', 'MYSQL', 'Scale', '0']}
I was not assigning it to any other variables like out1/2 in case you have more than 2 products
Here it is:
Code:
product = {'Product1': {'index': '1', '1': 'Book', '2': 'Pencil', '3': 'Pen','value': '1'},
'Product2': {'index': '2', '1': 'Marker', '2': 'MYSQL', '3': 'Scale','value': '0'}}
output_1 = {}
output_2 = {}
for key,val in product.items():
if (val['value'] == '0'):
output_1[key]=val
output_2[key]=val.values()
print(output_1)
print(output_2)
Output:
{'Product2': {'1': 'Marker', 'index': '2', '3': 'Scale', '2': 'MYSQL', 'value': '0'}}
{'Product2': ['Marker', '2', 'Scale', 'MYSQL', '0']}

append values to a dict in python

I have a list of dictionaries -
list1 = [{'id' : '1', 'b' : '2', 'c' : '3'}, {'id' : '4', 'b' : '5', 'c' : '6'}, {'id' : '7', 'b' : '8', 'c' : ''}]
Based on the value of c being null or not, I am making a call which returns -
list2 - {'d' : '30', 'id' : 1}, {'d': '25', 'id' : '4'}
Now I want to modify list1, so that the final list has the values of d for the ids which have c. For example -
list1 = [{'id' : '1', 'b' : '2', 'c' : '3', 'd' : '30'}, {'id' : '4', 'b' : '5', 'c' : '6', 'd' : '25'}, {'id' : '7', 'b' : '8', 'c' : ''}]
My approach -
for l in list2:
current_list = {}
for l2 in list1:
if l2['id'] == l['id']:
current_list = l2
break
if current_list:
current_list['d'] = l['d']
Here the actual dict is not getting modified. How can I modify the actual list? Also, is there a neater way to do this?
I'm not certain I understand what you are trying to accomplish. Your written description of your goal does not agree with you code. Based on the code, I'm guessing that you want to match up the data based on the id values.
# You've got some dicts.
dicts = [
{'id': '1', 'b': '2', 'c': '3'},
{'id': '4', 'b': '5', 'c': '6'},
{'id': '7', 'b': '8', 'c': ''},
]
# You've got some other dicts having the same IDs.
d_dicts = [
{'d': '30', 'id': '1'},
{'d': '25', 'id': '4'},
]
# Reorganize that data into a dict, keyed by ID.
dlookup = {d['id'] : d['d'] for d in d_dicts}
# Now add that lookup data to the main list of dicts.
for d in dicts:
i = d['id']
if i in dlookup:
d['d'] = dlookup[i]
Assuming mr FMc are correct, there is in python 3.5 a valid approach to merge dicts. Which in this case would led us to:
dicts = [
{'id': '1', 'b': '2', 'c': '3'},
{'id': '4', 'b': '5', 'c': '6'},
{'id': '7', 'b': '8', 'c': ''},
]
d_dicts = [
{'d': '30', 'id': '1'},
{'d': '25', 'id': '4'},
]
dicts = [{**d, **dict(*filter(lambda x: x["id"] == d["id"], d_dicts))} for d in dicts]
I like these kinda expressions instead of writing it all out, but it has the "benefit" of crashing instead of overwriting stuff when there is more then one dict with the same id. But my solution still overwrites values if there are duplicate keys silently. The inserted value being the value from whatever's second in the dict merge.

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