group dict emelents on lists based on key name - python

I want to group dict keys in list of dicts based on dict keys name. The dict keys alawys starts with number for example :
{'1_account_id': '5', '1_qty_in': '10.01', '1_uom_in': '1', '2_account_id': '23', '2_qty_in': '2.01', '2_uom_in': '1','3_account_id': '23', '3_qty_in': '2.01', '3_uom_in': '1' ,'some_x':1,'some_y':0}
I want to convert that to list grouped by this number like:
[{'1':{account_id': '5','qty_in':10.01,'uom_in':1}},{'2':{account_id': '23','qty_in':2.01,'uom_in':1}}....] etc
This numbers is not static.
I tried multiple solutions but it seems not good for performace:
Like i looped range for 0 to SOMEBIGNUMBER but this is not a good solution.
i also tried to loop throw the elements to extract the numbers first and then loop again to group but the dict myaybe come not clean as it has other not needed keys.
What should i do ?

So given the input d
d = {'1_account_id': '5', '1_qty_in': '10.01', '1_uom_in': '1', '2_account_id': '23', '2_qty_in': '2.01', '2_uom_in': '1','3_account_id': '23', '3_qty_in': '2.01', '3_uom_in': '1' ,'some_x':1,'some_y':0}
ans = {}
for key in d:
n,k = key.split('_', 1)
if n in ans:
ans[n][k] = d[key]
else:
ans[n] = {'account_id': 0, 'qty_in': 0, 'uom_in': 0}
ans[n][k] = d[key]
The above code produces the following output in which the entire output ans is a dictionary with the numbers as keys, and the values for each of the keys is also a dictionary, each containing 3 keys and corresponding values :
{'1': {'account_id': '5', 'qty_in': '10.01', 'uom_in': '1'},
'2': {'account_id': '23', 'qty_in': '2.01', 'uom_in': '1'},
'3': {'account_id': '23', 'qty_in': '2.01', 'uom_in': '1'},
'some': {'account_id': 0, 'qty_in': 0, 'uom_in': 0, 'x': 1, 'y': 0}}
Now to convert it into the list which you have shown in the desired output, use the follwing code. Here we convert the dictionary of dictionaries to a list containing dictionaries.
ans2 = []
for row in ans:
temp = {}
temp[row] = ans[row]
ans2.append(temp)
which produces the desired output :
[{'1': {'account_id': '5', 'qty_in': '10.01', 'uom_in': '1'}},
{'2': {'account_id': '23', 'qty_in': '2.01', 'uom_in': '1'}},
{'3': {'account_id': '23', 'qty_in': '2.01', 'uom_in': '1'}},
{'some': {'account_id': 0, 'qty_in': 0, 'uom_in': 0, 'x': 1, 'y': 0}}]
Hope this helps !

Mostafa.
You can run this code example to solve it:
example_dict = {'1_account_id': '5', '1_qty_in': '10.01', '1_uom_in': '1', '2_account_id': '23', '2_qty_in': '2.01', '2_uom_in': '1','3_account_id': '23', '3_qty_in': '2.01', '3_uom_in': '1' ,'some_x':1,'some_y':0}
desired_keys = ['account_id','qty_in','uom_in']
result = {}
for key,value in example_dict.items():
key_num, key_name = key.split('_', 1)
if key_name in desired_keys:
result.setdefault(key_num, {})[key_name] = value
print(result)
{'1': {'accountid': '5', 'qtyin': '10.01', 'uomin': '1'}, '2': {'accountid': '23', 'qtyin': '2.01', 'uomin': '1'}, '3': {'accountid': '23', 'qtyin': '2.01', 'uomin': '1'}, 'some': {'x': 1, 'y': 0}}
It will print (as you desired):
{'1': {'accountid': '5', 'qtyin': '10.01', 'uomin': '1'}, '2': {'accountid': '23', 'qtyin': '2.01', 'uomin': '1'}, '3': {'accountid': '23', 'qtyin': '2.01', 'uomin': '1'}, 'some': {'x': 1, 'y': 0}}
Tell me if you do not understand something.

Related

Change one dict/list python structure to another

I have the following data
data={
None: [
{'ne': '1', 'na': '1'},
{'ne': '2', 'na': '2'},
{'ne': '3', 'na': '3'},
{'ne': '4', 'na': '4'}
],
'AO': [
{'ne': '2', 'na': '2'},
{'ne': '6', 'na': '6'}
],
'NZ': [
{'ne': '1', 'na': '1'}
]
}
and I want to have a list from it like this:
[
{'ne': '1', 'na': '1', 'country': [None, 'NZ']},
{'ne': '2', 'na': '2', 'country': [None, 'AO']},
{'ne': '3', 'na': '3', 'country': [None]},
{'ne': '4', 'na': '4', 'country': [None]},
{'ne': '6', 'na': '6', 'country': ['AO']}
]
my code is doing it fine but it's far from being "pythonic" because I'm a newbie at python:
data = {None: [{'ne': '1', 'na': '1'}, {'ne': '2', 'na': '2'}, {'ne': '3', 'na': '3'}, {'ne': '4', 'na': '4'}], 'AO': [{'ne': '2', 'na': '2'}, {'ne': '6', 'na': '6'}], 'NZ': [{'ne': '1', 'na': '1'}]}
data_list = []
for k,d in data.items():
for dd in d:
dd['country'] = k
data_list.append(dd)
help_dict = {}
for item in data_list:
help_dict[item['ne']] = False
final_list = []
for idx, val in enumerate(data_list):
if not help_dict[val['ne']]:
val['country'] = [val['country']]
for idx2, val2 in enumerate(data_list):
if idx2 != idx and val['ne'] == val2['ne']:
val['country'].append(val2['country'])
help_dict[val['ne']] = True
final_list.append(val)
print(final_list)
can someone help me with a better way to do this?
new = [x for key,value in data.items() for x in value]
# remove duplicate dictionaries
new = [dict(t) for t in {tuple(d.items()) for d in new}]
for d in new:
d['country'] = [key for key,data in data.items() if d in data]
print(new)
>>> [{'ne': '2', 'na': '2', 'country': [None, 'AO']},
{'ne': '4', 'na': '4', 'country': [None]},
{'ne': '1', 'na': '1', 'country': [None, 'NZ']},
{'ne': '6', 'na': '6', 'country': ['AO']},
{'ne': '3', 'na': '3', 'country': [None]}]
If you want to preserve the order
new = [x for n,(key,value) in enumerate(data.items()) for x in value]
seen = set()
new_l = []
for d in new:
t = tuple(d.items())
if t not in seen:
seen.add(t)
new_l.append(d)
for d in new_l:
d['country'] = [key for key,data in data.items() if d in data]
print(new_l)
>>> [{'ne': '1', 'na': '1', 'country': [None, 'NZ']},
{'ne': '2', 'na': '2', 'country': [None, 'AO']},
{'ne': '3', 'na': '3', 'country': [None]},
{'ne': '4', 'na': '4', 'country': [None]},
{'ne': '6', 'na': '6', 'country': ['AO']}]
This is a really naive approach to solve your problem, due to it requiring that the inner dictionaries are sorted in the same order for it to "match" earlier found dictionaries.
For more complex dictionaries inside of the country this might not give correct results:
data={
None: [
{'ne': '1', 'na': '1'},
{'ne': '2', 'na': '2'},
{'ne': '3', 'na': '3'},
{'ne': '4', 'na': '4'}
],
'AO': [
{'ne': '2', 'na': '2'},
{'ne': '6', 'na': '6'}
],
'NZ': [
{'ne': '1', 'na': '1'}
]
}
d = {}
for country in data:
for dictionary in data[country]:
# Create a key that is a string of the dictionary, and value is dictionary plus country
x = d.setdefault(str(dictionary), dictionary | {"country": []})
# If you're using Python < 3.9, use this instead:
# x = d.setdefault(str(dictionary), {**dictionary, "country": []})
x["country"].append(country)
# pprint only used to represent data better
import pprint
pprint.pp(list(d.values()))
Output:
[{'ne': '1', 'na': '1', 'country': [None, 'NZ']},
{'ne': '2', 'na': '2', 'country': [None, 'AO']},
{'ne': '3', 'na': '3', 'country': [None]},
{'ne': '4', 'na': '4', 'country': [None]},
{'ne': '6', 'na': '6', 'country': ['AO']}]
Firstly, I'm assuming ne and na are always the same.
An optimal intermediate data structure is a dict with ne/na as keys and country lists as values:
{'1': [None, 'NZ'],
'2': [None, 'AO'],
'3': [None],
'4': [None],
'6': ['AO']}
Once you have that goal in mind, it's super simple to do it Pythonically:
inter = {}
for k, dicts in data.items():
for d in dicts:
inter.setdefault(d['ne'], []).append(k)
dict.setdefault() is used to get the value if it exists, or if not, set it to a default, which is an empty list here. It's functionally the same as this:
ne = d['ne']
if ne not in inter:
inter[ne] = []
inter[ne].append(k)
You could also use collections.defaultdict(list) to do the same thing even more easily.
And once you have that dict, you just need to unpack it into a list of dicts:
result = [{'ne': ne, 'na': ne, 'country': c} for ne, c in inter.items()]
Which becomes:
[{'ne': '1', 'na': '1', 'country': [None, 'NZ']},
{'ne': '2', 'na': '2', 'country': [None, 'AO']},
{'ne': '3', 'na': '3', 'country': [None]},
{'ne': '4', 'na': '4', 'country': [None]},
{'ne': '6', 'na': '6', 'country': ['AO']}]

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.

Python 3 Dictionary sorted with wrong output

I am currently doing an Assignment; however, I got some interesting output which confused me so much.
I am trying to sort the following dictionary:
result = {'A1': '9', 'A2': '14', 'A3': '16', 'A4': '0', 'B1': '53', 'B2': '267', 'B3': '75', 'B4': '22', 'C1': '19', 'C2': '407', 'C3': '171', 'C4': '56', 'C5': '10', 'D3': '47', 'D4': '34', 'D5': '10'}
My sorting code with Python 3 is the following : (only sorted by value)
sortedList = [v for v in sorted(result.values())]
The output is :
['0', '10', '10', '14', '16', '171', '19', '22', '267', '34', '407', '47', '53', '56', '75', '9']
which is not fully sorted. The output is quite strange.
Why it is happened like this?
I have used another dict to test like this:
testD = {'A':'5','B': '9','c': '8','d': '6'}
the output is right :
['5', '6', '8', '9']
Is there something wrong with my result dictionary or is there something I am missing?
Strings will be ordered with a lexical sort. To sort your data numerically, convert the values into integers first.
Strings are compared one character at a time, so '30' < '4' because '3' < '4'. You need to use a key parameter to get the comparison based on the numeric value, not the string characters.
Also, it's redundant to use a list comprehension on something that already returns a list.
sortedList = sorted(result.values(), key=int)
As the value of dictionary are string so there is lexical sorting based on ascii values.
As evident, you need the values to be sorted according to their integer values.
result = {'A1': '9', 'A2': '14', 'A3': '16', 'A4': '0', 'B1': '53', 'B2': '267', 'B3': '75', 'B4': '22', 'C1': '19', 'C2': '407', 'C3': '171', 'C4': '56', 'C5': '10', 'D3': '47', 'D4': '34', 'D5': '10'}
As mentioned in the comments by #AChampion, you can pass the sort value type by using key something like this :
sortedList = sorted(result.values(), key = int)
print(sortedList)
Or you can do something like this :
result_ints = dict((k,int(v)) for k,v in result.items())
sortedList = [str(v) for v in sorted(result_ints.values())]
print(sortedList)
Both of the above code snippets will result in :
['0', '9', '10', '10', '14', '16', '19', '22', '34', '47', '53', '56', '75', '171', '267', '407']
You can try this :
result = [{'A1': '9', 'A2': '14', 'A3': '16', 'A4': '0', 'B1': '53', 'B2': '267', 'B3': '75', 'B4': '22', 'C1': '19', 'C2': '407', 'C3': '171', 'C4': '56', 'C5': '10', 'D3': '47', 'D4': '34', 'D5': '10'}]
lst=[]
for item in result:
for key in item.keys():
lst.append(int(item.get(key)))
sortedList = [v for v in sorted(lst)]
print(sortedList)
Output: [0, 9, 10, 10, 14, 16, 19, 22, 34, 47, 53, 56, 75, 171, 267,
407]

How to convert list into a dictionary by python

old_list = [ ['ID0', 'ID1'], ['4', '8'], ['5', '6'] ]
I want convert list to new list
key = ['id', 'frame', 'length']
new_list = [{'id': 'ID0', 'frame': '4', 'length': '5'}, {'id': 'ID1', 'frame': '8', 'length': '6'}]
Here's a one-line approach:
>>> [{'id':x, 'frame':y, 'length':z} for x,y,z in zip(*old_list)]
[{'length': '5', 'frame': '4', 'id': 'ID0'}, {'length': '6', 'frame': '8', 'id': 'ID1'}]
new_list=[]
for x,y,z in zip(old_list[0],old_list[1], old_list[2]):
dict = {'id' : x, 'frame' : y, 'length': z}
new_list.append(dict)

How to convert string to dictionary into python

How can I convert a string
s = "1:5.9,1p5:7,2:10,4:18,8:40"
to a dictionary like this?
s = { '1':'5.9','1p5':'7','2':'10','4':'18','8':40'}
Use dict() and str.split:
>>> s = "1:5.9,1p5:7,2:10,4:18,8:40"
>>> dict(item.split(':') for item in s.split(','))
{'1': '5.9', '8': '40', '2': '10', '4': '18', '1p5': '7'}
Using a dict-comprehension:
>>> {k:v for k, v in (item.split(':') for item in s.split(','))}
{'1': '5.9', '8': '40', '2': '10', '4': '18', '1p5': '7'}

Categories

Resources