Here is a nested dictionary:
fdict = {}
fdict['apple'] = {}
fdict['banana'] = {}
fdict['apple']['green'] = 5
fdict['apple']['red'] = 0
fdict['banana']['light_yellow'] = 10
fdict['banana']['dark_yellow'] = 0
fdict['appraisal round'] = 1
{'apple': {'green': 5, 'red': 0},
'banana': {'light_yellow': 10, 'dark_yellow': 0},
'appraisal round': 1}
What is the most pythonic way to remove the key and value pairs where the value is zero from this nested dictionary such that the following dictionary results:
{'apple': {'green': 5}, 'banana': {'light_yellow': 10}, 'appraisal round': 1}
Note: The appraisal round key does not have a dictionary as a value itself.
Here is what I have implemented so far:
overall_dict = {}
for key in [key for key in fdict.keys() if key != 'appraisal round']:
new_dict = {k:v for k,v in fdict[key].items() if v != 0}
overall_dict[key] = new_dict
overall_dict['appraisal round'] = 1
However, using a temporary dictionary, constructing an entirely new dictionary and adding back in the appraisal round does not seem like a clean approach. Is there perhaps a way to amend the existing dictionary more effectively?
I suggest the following solution (it works for multiple nested levels):
fdict = {
'apple': {'green': 5, 'red': 0},
'banana': {'light_yellow': 10, 'dark_yellow': 0},
'appraisal round': 1
}
noZero = lambda d: { k1: noZero(v1) if isinstance(v1, dict) else v1 for k1, v1 in d.items() if v1 }
print(noZero(fdict)) # {'apple': {'green': 5}, 'banana': {'light_yellow': 10}, 'appraisal round': 1}
Assuming that You do not know the key that has non-dict type, a solution could be:
fdict = {}
fdict['apple'] = {}
fdict['banana'] = {}
fdict['apple']['green'] = 5
fdict['apple']['red'] = 0
fdict['banana']['light_yellow'] = 10
fdict['banana']['dark_yellow'] = 0
fdict['appraisal round'] = 1
def non_zero_value(item):
k, v = item
return v != 0
result = {}
for k, v in fdict.items():
if isinstance(v, dict):
result[k] = dict(filter(non_zero_value, v.items()))
else:
result[k] = v
print(result)
# {'apple': {'green': 5}, 'banana': {'light_yellow': 10}, 'appraisal round': 1}
Related
i have these two dictionaries and i would like to add the data dict1 in dict2. how can i do that with python ?
dict1 = {
213688169:
{'stationCode': '6003',
'station_id': 213688169,
'num_bikes_available': 10,
'numBikesAvailable': 10,
'num_bikes_available_types': [{'mechanical': 10}, {'ebike': 0}],
'num_docks_available': 10,
'numDocksAvailable': 10,
'is_installed': 1,
'is_returning': 1,
'is_renting': 1,
'last_reported': 1619207955}
}
dict2 = {
213688169:
{'station_id': 213688169,
'name': 'Benjamin Godard - Victor Hugo',
'lat': 48.865983,
'lon': 2.275725,
'capacity': 35,
'stationCode': '16107'}
}
i tried this but it's too long and complicated :
donnees=[]
for i in stations:
for j in velib_description :
if i['station_id'] == j['station_id']:
List={}
List['name'] = i['name']
List['lat'] = i['lat']
List['lon'] = i['lon']
List['capacity'] = i['capacity']
List['num_bikes_available'] = j['num_bikes_available']
List['num_bikes_available_types'] = j['num_bikes_available_types']
List['last_reported'] = j['last_reported']
donnees.append(List)
I want to add in dict_2 = {num_bikes_available', 'num_bikes_available_types', last_reported': 1619207955 }
thank you
Perhaps you want dict.update?
dict1 = {
213688169: {
"stationCode": "6003",
"station_id": 213688169,
"num_bikes_available": 10,
"numBikesAvailable": 10,
"num_bikes_available_types": [{"mechanical": 10}, {"ebike": 0}],
"num_docks_available": 10,
"numDocksAvailable": 10,
"is_installed": 1,
"is_returning": 1,
"is_renting": 1,
"last_reported": 1619207955,
}
}
dict2 = {
213688169: {
"station_id": 213688169,
"name": "Benjamin Godard - Victor Hugo",
"lat": 48.865983,
"lon": 2.275725,
"capacity": 35,
"stationCode": "16107",
}
}
dict1_item = dict1[213688169]
dict2[213688169].update({k: v for k, v in dict1_item.items() if k not in dict2})
dict2
This prints
{213688169: {'station_id': 213688169,
'name': 'Benjamin Godard - Victor Hugo',
'lat': 48.865983,
'lon': 2.275725,
'capacity': 35,
'stationCode': '6003',
'num_bikes_available': 10,
'numBikesAvailable': 10,
'num_bikes_available_types': [{'mechanical': 10}, {'ebike': 0}],
'num_docks_available': 10,
'numDocksAvailable': 10,
'is_installed': 1,
'is_returning': 1,
'is_renting': 1,
'last_reported': 1619207955}}
We're using a comprehension to get all the key/values from the first dictionary that aren't in the second, then updating the second dictionary. We have to do a little munging with the 213688169 key because your dictionary is nested.
You can use a dictionary comprehension with a dictionary merge trick:
dict3 = {k: {**v, **dict2[k]} for k, v in dict1.items()}
or for Python 3.9 and later:
dict3 = {k: v | dict2[k] for k, v in dict1.items()}
I've two nested dictionaries of variable depth orig_dict , new_dict
orig_dict = {"a":1, "b":[{"c":{"d":2}, "e":[{"q":12}, {"a":2}]}, {"h":[1,2,3,4]}], "e":{"we":12}}
new_dict = {"a":2, "b":[{"c":{"d":3}, "e":[{"q":120}, {"a":2}, {"x":10000}]}], "e":{"we":12}, "f":100}
Here new_dict is the updates needed to be done in orig_dict,
if key doesn't exists add that key-val to orig_dict
if key in orig_dict doesn't exists in new_dict then delete key-val from orig_dict
if key matches skip changes
I've code which will process only dictionaries as values but not list
import collections
def map_and_update(orig_dict, new_dict):
for key, val in new_dict.items():
if isinstance(val, collections.Mapping):
tmp = updatee(orig_dict.get(key, { }), val)
orig_dict[key] = tmp
else:
orig_dict[key] = new_dict[key]
return orig_dict
this will result:
{'a': 2, 'b': [{'c': {'d': 3}, 'e': [{'q': 120}, {'a': 2}, {'x': 10000}]}], 'e': {'we': 12}, 'f': 100}
but i expect
{'a': 1, 'b': [{'c': {'d': 2}, 'e': [{'q': 12}, {'a': 2}, {'x': 10000}]}], 'e': {'we': 12}, 'f': 100}
Note: Above i'm just copying the value of new_dict to orig_dict if i
see a list, but it should parse through list even and check whether
dict exists or not if exists then again do map
This code of mine is maybe not very readable, but at least it did finish the job. I dont have time right now, but if anyone want me to explain, i will edit this post later.
orig_dict = {"a":1, "b":[{"c":{"d":2}, "e":[{"q":12}, {"a":2}]}, {"h":[1,2,3,4]}], "e":{"we":12}}
new_dict = {"a":2, "b":[{"c":{"d":3}, "e":[{"q":120}, {"a":2}, {"x":10000}]}], "e":{"we":12}, "f":100}
for key, value in new_dict.items():
if isinstance(value, list):
for key1, value1 in new_dict['b'][0].items():
if orig_dict['b'][0].get(key1):
pass
if len(new_dict['b']) != len(orig_dict['b']):
del orig_dict['b'][len(new_dict['b']):]
length = len(new_dict['b'][0]['e'])
for i in range(length):
for k, v in new_dict['b'][0]['e'][i].items():
try:
if orig_dict['b'][0]['e'][i].get(k):
pass
else:
orig_dict['b'][0]['e'][i][k]=v
except:
orig_dict['b'][0]['e'].append({k:v})
elif orig_dict.get(key):
pass
else:
orig_dict[key]=value
print(orig_dict)
I need to define a function, group_dictionaries, that will take a list of dictionaries and return a list of dictionaries which contain the same values for EACH key of the list of keys. The "lonely" dictionaries will be deleted.
Here is an example:
my_list=[
{'id':'id1', 'key1':value_x, 'key2': value_y, 'key3':value_z},
{'id':'id3', 'key2 :value_u, 'key3': value_v},
{'id':'id2', 'key1':value_x, 'key3':value_z, 'key4': value_t},
{'id':'id4', 'key1':value_w, 'key2':value_s, 'key3':value_v}
]
group_dictionary(my_list, list_of_keys=['key1', 'key3'])
#result: the only dictionaries that have key1 AND key3 in common are:
[
{'id':'id1', 'key1':value_x, 'key2': value_y, 'key3':value_z, 'group':0},
{'id':'id2', 'key1':value_x, 'key3':value_z, 'key4': value_t, 'group':0}
]
group_dictionary(my_list, list_of_keys=['key3'])
#result the dictionaries that have key3 in common are divided in two groups
#of different values: group 0 has value_z and group1 has value_v
[
{'id':'id1', 'key1':value_x, 'key2': value_y, 'key3':value_z, 'group':0},
{'id':'id2', 'key1':value_x, 'key3':value_z, 'key4': value_t, 'group':0},
{'id':'id3', 'key2 :value_u, 'key3': value_v, 'group':1},
{'id':'id4', 'key1':value_w, 'key2':value_s, 'key3':value_v, 'group':1}
]
As you can see:
The function creates a key labeled 'Group' which is an integer
starting from 0. This key is assigned to each "group" of dictionaries
(by group I mean dictionaries whose keys corresponding to the list
of keys match exactly for EACH key)
The function deletes the dictionaries which don't have "groups".
The existing data set on which I am working contains a UNIQUE id for
each dictionary. This may be useful to create the function.
Non existing keys prevent a dictionary from being candidate.
I am concerned about run-time; the actual list contains 80,000 dictionaries of 35 keys each in average. The complexity of the algorithm is likely to be n² (80,000²). Any optimization in the code is welcome.
I believe this will work, it's written in Python3, I haven't optimized it, but it could be a good starting point if it's not fast enough.
list_of_dicts = [
{'id':'id1', 'key1':'value_x', 'key2': 'value_y', 'key3':'value_z'},
{'id':'id3', 'key2' :'value_u', 'key3': 'value_v'},
{'id':'id2', 'key1':'value_x', 'key3':'value_z', 'key4': 'value_t'},
{'id':'id4', 'key1':'value_w', 'key2':'value_s', 'key3':'value_v'}
]
# Since we can't have objects as keys, make the values we're looking for into a string, and use that as the key.
def make_value_key(d, list_of_keys):
res = ""
for k in list_of_keys:
res += str(d[k])
return res
def group_dictionary(list_of_dicts, list_of_keys):
group_vals = {}
current_max_group = 0
dicts_to_remove = []
for i,d in enumerate(list_of_dicts):
# If dict doesn't have all keys mark for removal.
if not all(k in d for k in list_of_keys):
dicts_to_remove.append(i)
else:
value_key = make_value_key(d, list_of_keys)
# If value key exists assign group otherwise make new group.
if value_key in group_vals:
d['group'] = group_vals[value_key]
else:
group_vals[value_key] = current_max_group
d['group'] = current_max_group
current_max_group += 1
list_of_dicts = [i for j, i in enumerate(list_of_dicts) if j not in dicts_to_remove]
return list_of_dicts
list_of_keys=['key1','key3']
print(group_dictionary(list_of_dicts, list_of_keys))
print()
list_of_keys=['key3']
print(group_dictionary(list_of_dicts, list_of_keys))
Output:
[{'key3': 'value_z', 'key1': 'value_x', 'group': 0, 'key2': 'value_y', 'id': 'id1'},
{'key3': 'value_z', 'key1': 'value_x', 'key4': 'value_t', 'group': 0, 'id': 'id2'},
{'key3': 'value_v', 'key1': 'value_w', 'group': 1, 'key2': 'value_s', 'id': 'id4'}]
[{'key3': 'value_z', 'key1': 'value_x', 'group': 0, 'key2': 'value_y', 'id': 'id1'},
{'group': 1, 'key3': 'value_v', 'key2': 'value_u', 'id': 'id3'},
{'key3': 'value_z', 'key1': 'value_x', 'key4': 'value_t', 'group': 0, 'id': 'id2'},
{'key3': 'value_v', 'key1': 'value_w', 'group': 1, 'key2': 'value_s', 'id': 'id4'}]
Optimization 1:
Instead of iterating all keys to check if they exist, instead we can just fail while making the value-key and return an empty string, which would mark the dict for deletion:
def make_value_key(d, list_of_keys):
res = ""
for k in list_of_keys:
if not k in d:
return ""
res += str(d[k])
return res
def group_dictionary(list_of_dicts, list_of_keys):
group_vals = {}
current_max_group = 0
dicts_to_remove = []
for i,d in enumerate(list_of_dicts):
value_key = make_value_key(d, list_of_keys)
if value_key == "":
dicts_to_remove.append(i)
continue
if value_key in group_vals:
d['group'] = group_vals[value_key]
else:
group_vals[value_key] = current_max_group
d['group'] = current_max_group
current_max_group += 1
list_of_dicts = [i for j, i in enumerate(list_of_dicts) if j not in dicts_to_remove]
return list_of_dicts
Groups have to bigger than 1:
This uses a second dict to keep track of group sizes, and then checks whether groups are smaller than 2 to mark them for removal.
def make_value_key(d, list_of_keys):
res = ""
for k in list_of_keys:
if not k in d:
return ""
res += str(d[k])
return res
def group_dictionary(list_of_dicts, list_of_keys):
group_vals = {}
group_count = {}
current_max_group = 0
indices_to_remove = []
for i,d in enumerate(list_of_dicts):
value_key = make_value_key(d, list_of_keys)
if value_key == "":
indices_to_remove.append(i)
continue
if value_key in group_vals:
d['group'] = group_vals[value_key]
# Second group member seen, remove from count dict.
group_count.pop(d['group'], None)
else:
group_vals[value_key] = current_max_group
d['group'] = current_max_group
# First time seen, add to count dict.
group_count[current_max_group] = i
current_max_group += 1
indices_to_remove.extend(group_count.values())
return [i for j, i in enumerate(list_of_dicts) if j not in indices_to_remove]
Output:
[{'key2': 'value_y', 'group': 0, 'id': 'id1', 'key1': 'value_x', 'key3': 'value_z'},
{'key4': 'value_t', 'group': 0, 'id': 'id2', 'key1': 'value_x', 'key3': 'value_z'}]
[{'key2': 'value_y', 'group': 0, 'id': 'id1', 'key1': 'value_x', 'key3': 'value_z'}, {'group': 1, 'id': 'id3', 'key2': 'value_u', 'key3': 'value_v'}, {'key4': 'value_t', 'group': 0, 'id': 'id2', 'key1': 'value_x', 'key3': 'value_z'}, {'key2': 'value_s', 'group': 1, 'id': 'id4', 'key1': 'value_w', 'key3': 'value_v'}]
Optimization 2:
You can go from O(n^2) (loop through the dict list once to calculate and once to delete) to O(n*m log m) (loop through the dict list once and loop through sorted removed indices):
def make_value_key(d, list_of_keys):
res = ""
for k in list_of_keys:
if not k in d:
return ""
res += str(d[k])
return res
def group_dictionary(list_of_dicts, list_of_keys):
group_vals = {}
group_count = {}
current_max_group = 0
indices_to_remove = []
for i,d in enumerate(list_of_dicts):
value_key = make_value_key(d, list_of_keys)
if value_key == "":
indices_to_remove.append(i)
continue
if value_key in group_vals:
d['group'] = group_vals[value_key]
# Second group member seen, remove from count dict.
group_count.pop(d['group'], None)
else:
group_vals[value_key] = current_max_group
d['group'] = current_max_group
# First time seen, add to count dict.
group_count[current_max_group] = i
current_max_group += 1
indices_to_remove.extend(group_count.values())
for index in sorted(indices_to_remove, reverse=True):
del list_of_dicts[index]
return list_of_dicts
This is straightforward enough; first, you will want some way to easily serialize the relevant data in the dict. I'll use this (very simple) approach, but depending on the complexity of your data you might need to come up with something more robust:
def serialize(d, keys):
return ','.join([d[key] for key in keys])
Then, you simply store all these serialized values in a list. The index of the value in the list is the ID of your group.
def group_dictionary(dicts, keys):
groups = []
result = []
for d in dicts:
# skip over dictionaries that don't have all keys
if any(key not in d for key in keys):
continue
# get the serialized data
serialized_data = serialize(d, keys)
# if we've encountered a new set of data, create a new group!
if serialized_data not in groups:
groups.append(serialized_data)
# augment the dictionary with the group id
d['group'] = groups.index(serialized_data)
# and add it to the list of returned dictionaries
result.append(d)
return result
I am having trouble with the following code. I need to add a key to a python dictionary if it does not exist, and if it does then I need to add to the value.
My output should look like this.
{'STA': {'value':-62**.***, 'count': 4}}.....
But I'm getting this instead.
{'STA': {'value': -1194.14562548, 'count': 0}},
{'STA': {'value': -5122.985396600001, 'count': 0}},
{'STA': {'value': 25.2293, 'count': 0}},
{'STA': {'value': 34.0099, 'count': 0}},
What am I doing wrong?
new_dict = []
for item in sales_orders['progress_output']:
ex = (list(filter(lambda ex:ex['_branch_shortname'].strip() == item['_branch_shortname'].strip(), expenses)))
value = float(item['gross_profit'].strip().replace(',', '')) - (float(item['load_factor_extended'].strip().replace(',', '')) * float(ex[0]['value']))
branch = item['_branch_shortname'].strip()
if branch in new_dict:
new_dict[branch]['value'] += value
new_dict[branch]['count'] += 1
else:
new_dict.append({branch: {'value': value, 'count': 0}})
#print(item['_branch_shortname'], value)
print(new_dict)
You can use setdefault to ensure there is a default value, setdefault returns the value if it already exists for key (first parameter) or adds the default (second parameter) and returns it:
new_dict = {}
for ...:
b = new_dict.setdefault(branch, {'value': 0, 'count': 0})
b['value'] += value
b['count'] += 1
You can also use a defaultdict:
from collections import defaultdict
new_dict = defaultdict(lambda: {'value': 0, 'count': 0})
for ...:
new_dict[branch]['value'] += value
new_dict[branch]['count'] += 1
I have a dictionary that looks like this:
{'items': [{'id': 1}, {'id': 2}, {'id': 3}]}
and I'm looking for a way to directly get the inner dictionary with id = 1.
Is there a way to reach this other than looping the list items and comparing the id?
first_with_id_or_none = \
next((value for value in dictionary['items'] if value['id'] == 1), None)
You will have to loop through the list. The good news is is that you can use a generator expression with next() to do that looping:
yourdict = next(d for d in somedict['items'] if d['id'] == 1)
This can raise a StopIteration exception if there is no such matching dictionary.
Use
yourdict = next((d for d in somedict['items'] if d['id'] == 1), None)
to return a default instead for that edge-case (here None is used, but pick what you need).
Make it into a function:
def get_inner_dict_with_value(D, key, value):
for k, v in D.items():
for d in v:
if d.get(key) == value:
return d
else:
raise ValueError('the dictionary was not found')
With explanation:
def get_inner_dict_with_value(D, key, value):
for k, v in D.items(): # loop the dictionary
# k = 'items'
# v = [{'id': 1}, {'id': 2}, {'id': 3}]
for d in v: # gets each inner dictionary
if d.get(key) == value: # find what you look for
return d # return it
else: # none of the inner dictionaries had what you wanted
raise ValueError('the dictionary was not found') # oh no!
Running it:
>>> get_inner_dict_with_value({'items': [{'id': 1}, {'id': 2}, {'id': 3}]}, 'id', 1)
{'id': 1}
Another method:
def get_inner_dict_with_value2(D, key, value):
try:
return next((d for l in D.values() for d in l if d.get(key) == value))
except StopIteration:
raise ValueError('the dictionary was not found')
>>> get_inner_dict_with_value2({'items': [{'id': 1}, {'id': 2}, {'id': 3}]}, 'id', 1)
{'id': 1}