Let's say we have:
from collections import defaultdict
original_dict = { 'somegroupofelements':{'name':1, 'group':1 ,'results':[1,2,3,4]}, 'somegroupofelements2':{'name':2, 'group': 2 ,'results':[1,2,3,4]}, 'somegroupofelements3':{'name':3, 'group':3 ,'results':[1,2,3,4]} }
new_dict = defaultdict(list)
for key, value in original_dict.iteritems():
# i need to organize things grouped for making the right latex tables
# and for updating some values...
value['key']=key
new_dict[value['group']].append(value)
I want that new_dict, after I've done my work, to be organized again just like the original_dict? Like reconstruct the original_dict from the new_dict.
So you end up with a dictionary in the form:
>>> d = { 100 : [{'name':1, 'group':100, 'key':'group1'},
... {'name':2, 'group':100, 'key':'group2'}],
... 200 : [{'name':3, 'group':200, 'key':'group3'}] }
Which can be transformed back into a dictionary using a dict comprehension:
>>> orig = { x['key']:x for v in d.values() for x in v }
>>> orig
{'group1': {'group': 100, 'name': 1, 'key': 'group1'},
'group3': {'group': 200, 'name': 3, 'key': 'group3'},
'group2': {'group': 100, 'name': 2, 'key': 'group2'}}
If you want, you can then delete the superflous key field of the items:
>>> for v in orig.values(): del v['key']
...
>>> orig
{'group1': {'group': 100, 'name': 1},
'group3': {'group': 200, 'name': 3},
'group2': {'group': 100, 'name': 2}}
Related
How to iterate over a dictionary / JSON using a dynamic query.
For example consider the below dict
dict = {'Adam': {
'English': {
'Score': 99,
'Time': 3400,
'Classes': 4},
'Math': {
'Score': 45,
'Time': 779,
'Classes': 5}},
'Tim': {
'English': {
'Score': 74,
'Time': 12,
'Classes': 99},
'Math': {
'Score': 12,
'Time': 333,
'Classes': 1}}
}
I want to set the value of a given path for example
path = '/Adam/English/Score'
new_value = 87
Note that the value assigned could be another dict as well for example
path = '/Adam/English'
new_value = {'Score': 11,
'Time': 2,
'Classes': 9}
Any help would be useful.
Edit: Below is my attempt
keys = path.split('/')[1:]
new_data = None
for key in keys:
if new_data is None:
new_data = dict[key]
else:
new_data = new_data[key]
new_data = new_value
print(dict)
But here the dict still has the old value
I made some assumptions, for example that '/' is not part of any dict-keys and that the path must be valid. Adjust the function as needed.
def deep_set(d, path, value):
sep = '/'
*trail, last = path.strip(sep).split(sep)
for part in trail:
d = d[part]
d[last] = value
Demo:
>>> d = {'a': 1}
>>> deep_set(d, 'a', 2)
>>> d
{'a': 2}
>>> d = {'a': {'b': 1}}
>>> deep_set(d, 'a/b', 2)
>>> d
{'a': {'b': 2}}
edit:
Note that if there are consecutive '/' characters then the empty string will be looked up as a dict key. e.g.
'a/b//c'.split('/') -> ['a', 'b', '', 'c']
It's unclear whether you want to treat leading/trailling '/' characters as part of the path or not (in my function, they are removed with str.strip). Again, adjust as needed.
I have a list as:
my_list = ["10", "12", "32", "23"]
and a dictionary as:
my_dict = {
'one': {'index': 0, 'sec_key': 'AB', 'id': '10'},
'two': {'index': 0, 'sec_key': 'CD', 'id': '12'},
'three': {'index': 0, 'sec_key': 'EF', 'id': '32'}
}
I want to keep a dictionary say final_dict which will have content of my_dict only if the id is present in my_list. I tried to do that by:
sf_dict = dict()
for list_id in my_list:
for key, value in my_dict.items():
if list_id == value['id']:
print("Values : {} {} {}".format(value['index'], value['sec_key'], value['id']))
sf_dict[key] = key
sf_dict[key]['index'] = value['index']
sf_dict[key]['sec_key'] = value['sec_key']
sf_dict[key]['id'] = value['id']
print(sf_dict)
I'm able to print the values but the assigning of those values is failing due to the error as:
TypeError: 'str' object does not support item assignment
Where am I making mistake?
You can use a dictionary comprehension to loop over your my_dict and check if the value stored in id is in your list.
my_list = ["10", "12", "32", "23"]
my_dict = {
'one': {'index': 0, 'sec_key': 'AB', 'id': '10'},
'two': {'index': 0, 'sec_key': 'CD', 'id': '12'},
'three': {'index': 0, 'sec_key': 'EF', 'id': '32'}
}
result = {key:value for key, value in my_dict.items() if value['id'] in my_list}
#{'one': {'index': 0, 'sec_key': 'AB', 'id': '10'}, 'two': {'index': 0, 'sec_key': 'CD', 'id': '12'}, 'three': {'index': 0, 'sec_key': 'EF', 'id': '32'}}
my_list = ["10"]
#{'one': {'index': 0, 'sec_key': 'AB', 'id': '10'}}
The already given answers should do the trick, altough maybe it is important to get why your approach isn't right.
The problem with what you did is that when you do sf_dict[key] = key, you're setting the value of the dictionary for the key key with the value key. But key is a string, thus, when you later try sf_dict[key]['index'] = value['index'] it tells you that you cannot assign on top a string.
Try to remove these lines
sf_dict[key]['index'] = value['index']
sf_dict[key]['sec_key'] = value['sec_key']
sf_dict[key]['id'] = value['id']
and simply replace them with sf_dict[key] = value. This way, when you find the id you're searching for, you assign all the dictionary at once to the corresponding key.
Here is one solution:
final_dict={}
for i in my_dict:
if my_dict[i]['id'] in my_list:
final_dict[i]=my_dict[i]
Example:
my_list = ["10", "32", "23"]
Output:
print(final_dict)
{'one': {'index': 0, 'sec_key': 'AB', 'id': '10'}, 'three': {'index': 0, 'sec_key': 'EF', 'id': '32'}}
for list_id in my_list:
for key, value in my_dict.items():
if list_id == value['id']:
print("Values : {} {} {}".format(value['index'], value['sec_key'], value['id']))
sf_dict[key] = {'index':value['index'],'sec_key':value['sec_key'],'id':value['id']}
print(sf_dict)
the code should be like this.
#problem is here
sf_dict[key] = key
above line means sf_dict = {'one': 'one'} for example
so next lines mean 'one'['anything'] =
so using index to assign into str is not allowed.
better solution is already answered above, just pointing issue in exist code
I have a dict as follows.
dict = {'P': ['Demo'], 'Q': ['PMS']}
And I have a list of Dict as follows.
all = [{'Name': 'PMS'}, {'Name': 'Demo'}]
I need to have the dict's respective value in all as bellow.
new_list = [{'Name': 'PMS','Code': 'Q'}, {'Name': 'Demo','Code': 'P'}]
In this specific case, given that the values are just strings and therefore hashable objects, you can use a dictionary of reverse mappings. Be aware that it could not be used if the values were not hashable.
dct = {'P': ['Demo'], 'Q': ['PMS']}
all = [{'Name': 'PMS'}, {'Name': 'Demo'}]
reverse_mapping = {v[0]:k for k, v in dct.items()}
new_list = [d.copy() for d in all]
for d in new_list:
d['Code'] = reverse_mapping[d['Name']]
print(new_list)
This gives:
[{'Name': 'PMS', 'Code': 'Q'}, {'Name': 'Demo', 'Code': 'P'}]
dct = {'P': ['Demo'], 'Q': ['PMS']}
all_ = [{'Name': 'PMS'}, {'Name': 'Demo'}]
out = [dict(**l, Code=next(k for k, v in dct.items() if l['Name'] in v)) for l in all_]
print(out)
Prints:
[{'Name': 'PMS', 'Code': 'Q'}, {'Name': 'Demo', 'Code': 'P'}]
Or: you can make temporary dictionary:
tmp = {v[0]:k for k, v in dct.items()}
out = [dict(**l, Code=tmp[l['Name']]) for l in all_]
print(out)
You could make an inverted dictionary of codes, then go through the list of dictionaries and add the codes in:
codes = {"P": ["Demo"], "Q": ["PMS"]}
lst = [{"Name": "PMS"}, {"Name": "Demo"}]
inverted_codes = {value: key for key, values in codes.items() for value in values}
# {'Demo': 'P', 'PMS': 'Q'}
for dic in lst:
code = dic["Name"]
dic["Code"] = inverted_codes[code]
print(lst)
Output
[{'Name': 'PMS', 'Code': 'Q'}, {'Name': 'Demo', 'Code': 'P'}]
For example, I have below 2 dictionaries,
dict1 = [{'id': 1, 'name': 'BOB'}, {'id': 2, 'name': 'DOD'}]
dict2 = [{'idd': 1, 'comp': 'BB', }, {'idd': 1, 'work': 'pent'}, {'idd': 2, 'comp': 'DD'}]
And I want below output -
dict1 = [
{
'id': 1,
'name': 'BOB',
'Details:[
{
'idd': 1,
'comp': 'BB'
},
{
'idd': 1,
'work': 'pent'
}
]
},
{
'id': 2,
'name': 'DOD',
'Details':[
{
'idd': 2,
'comp': 'DD'
}
]
}
]
I want to get the above result, using dictionary zip or ordereddict
Convert the dict1 to a real dict, with the id as key, and add an empty Details list to each entry. Then, iterate dict2 and and the missing elements.
dict1 = {item['id']: {**item, **{'Details': []}} for item in dict1}
for item in dict2:
item = dict(item)
_id = item.pop('idd')
temp[_id]['Details'].append(item)
dict1 = [item for item in dict1.values()]
I have a list of dictionaries and I wanted to group the data. I used the following:
group_list = []
for key, items in itertools.groupby(res, operator.itemgetter('dept')):
group_list.append({key:list(items)})
For data that looks like this
[{'dept':1, 'age':10, 'name':'Sam'},
{'dept':1, 'age':12, 'name':'John'},
.
.
.
{'dept':2,'age':20, 'name':'Mary'},
{'dept':2,'age':11, 'name':'Mark'},
{'dept':2,'age':11, 'name':'Tom'}]
the output would be:
[{1:[{'dept':1, 'age':10, 'name':'Sam'},
{'dept':1, 'age':12, 'name':'John'}],
{2:[{'dept':2,'age':20, 'name':'Mary'},
{'dept':2,'age':11, 'name':'Mark'},
{'dept':2,'age':11, 'name':'Tom'}]
...
]
Now if I want to group using multiple keys say 'dept' and 'age', the above mentioned method returns
[{(2, 20): [{'age': 20, 'dept': 2, 'name': 'Mary'}]},
{(2, 11): [{'age': 11, 'dept': 2, 'name': 'Mark'},
{'age': 11, 'dept': 2, 'name': 'Tom'}]},
{(1, 10): [{'age': 10, 'dept': 1, 'name': 'Sam'}]},
{(1, 12): [{'age': 12, 'dept': 1, 'name': 'John'}]}]
The desired output would be:
[
{
2: {
20: [
{
'age': 20,
'dept': 2,
'name': 'Mary'
}
]
},
{
11: [
{
'age': 11,
'dept': 2,
'name': 'Mark'
},
{
'age': 11,
'dept': 2,
'name': 'Tom'
}
]
}
},
{
1: {
10: [
{
'age': 10,
'dept': 1,
'name': 'Sam'
}
]
},
{
12: [
{
'age': 12,
'dept': 1,
'name': 'John'
}
]
}
}
]
Can it be done with itertools? Or do I need to write that code myself?
Absolutely. You just need to apply itertools.groupby() for the second criterion first, then the other.
You would need to write a (probably recursive) bit of code to do this yourself - itertools doesn't have a tree-builder in it.
Thanks everyone for your help. Here is how I did it:
import itertools, operator
l = [{'dept':1, 'age':10, 'name':'Sam'},
{'dept':1, 'age':12, 'name':'John'},
{'dept':2,'age':20, 'name':'Mary'},
{'dept':2,'age':11, 'name':'Mark'},
{'dept':2,'age':11, 'name':'Tom'}]
groups = ['dept', 'age', 'name']
groups.reverse()
def hierachical_data(data, groups):
g = groups[-1]
g_list = []
for key, items in itertools.groupby(data, operator.itemgetter(g)):
g_list.append({key:list(items)})
groups = groups[0:-1]
if(len(groups) != 0):
for e in g_list:
for k,v in e.items():
e[k] = hierachical_data(v, groups)
return g_list
print hierachical_data(l, groups)