How to sum specific values within dictionary in python - python

I have the following nested dictionary and need to figure out how to sum all 'qty'.
data1 = {
'Batch1': {
'Pink': {'qty': 25, 'ordered': 15},
'Blue': {'qty': 18, 'ordered': 20}
},
'Batch2': {
'Coke': {'qty': 50, 'ordered': 100},
'Sprite': {'qty': 30, 'ordered': 25}
}
}
So the outcomes would be 123.

You can use sum:
data1 = {'Batch1': {'Pink': {'qty': 25, 'ordered':15}, 'Blue': {'qty':18, 'ordered':20}}, 'Batch2': {'Coke': {'qty': 50, 'ordered': 100},'Sprite': {'qty':30, 'ordered':25}}}
result = sum(b['qty'] for c in data1.values() for b in c.values())
Output:
123

Your data1 was formatted funny, so here's what I used:
{'Batch1': {'Blue': {'ordered': 20, 'qty': 18},
'Pink': {'ordered': 15, 'qty': 25}},
'Batch2': {'Coke': {'ordered': 100, 'qty': 50},
'Sprite': {'ordered': 25, 'qty': 30}}}
If you're not sure how deeply nested your dict will be, you can write a function to recursively traverse the nested dict looking for the qty key and sum the values:
def find_key_vals(query_key, base_dict):
values = []
for k, v in base_dict.items():
if k == query_key:
values.append(v)
elif isinstance(v, dict):
values += find_key_vals(query_key, v)
return values
find_key_vals('qty', data1)
# => [50, 30, 25, 18]
sum(find_key_vals('qty', data1))
# => 123

Related

Get list of dictionaries based on their key values

I have several dictionaries, let's say 5:
dict1={'Age': 20, 'Name': 'Bob'}
dict2={'Age': 10, 'Name': 'Ane'}
dict3={'Age': 40, 'Name': 'Lee'}
dict4={'Age': 50, 'Name': 'Rob'}
dict5={'Age': 30, 'Name': 'Sia'}
and
arr=[50,40,30,20,10]
Can I get a list of dictionaries based on the values of age in arr?
Desired output:
[dict4,dict3,dict5,dict1,dict2]
Try using a lambda to sort by a certain property (in this case, Age):
Code:
dict1={'Age': 20, 'Name': 'Bob'}
dict2={'Age': 10, 'Name': 'Ane'}
dict3={'Age': 40, 'Name': 'Lee'}
dict4={'Age': 50, 'Name': 'Rob'}
dict5={'Age': 30, 'Name': 'Sia'}
dicts = [dict1, dict2, dict3, dict4, dict5]
dicts.sort(reverse=True, key=lambda x: x['Age'])
print(dicts)
Output:
[{'Age': 50, 'Name': 'Rob'}, {'Age': 40, 'Name': 'Lee'}, {'Age': 30, 'Name': 'Sia'}, {'Age': 20, 'Name': 'Bob'}, {'Age': 10, 'Name': 'Ane'}]
Based on your comments:
arr = [50, 40, 30, 20, 10]
dicts = [dict1, dict2, dict3, dict4, dict5]
dicts = {d["Age"]: d for d in dicts}
dicts = [dicts[v] for v in arr]
print(dicts)
Prints:
[
{"Age": 50, "Name": "Rob"},
{"Age": 40, "Name": "Lee"},
{"Age": 30, "Name": "Sia"},
{"Age": 20, "Name": "Bob"},
{"Age": 10, "Name": "Ane"},
]
If the expected output is the dict names and not the values, you can create a mapping between Age and the dict name and iterate through arr and get the name of the dict by its age:
dict1 = {'Age': 20, 'Name': 'Bob'}
dict2 = {'Age': 10, 'Name': 'Ane'}
dict3 = {'Age': 40, 'Name': 'Lee'}
dict4 = {'Age': 50, 'Name': 'Rob'}
dict5 = {'Age': 30, 'Name': 'Sia'}
arr = [50, 40, 30, 20, 10]
age_to_dict_name = {globals()[x]['Age']: x for x in globals() if x.startswith("dict")}
expected_output = [age_to_dict_name[x] for x in arr]
print(expected_output) # ['dict4', 'dict3', 'dict5', 'dict1', 'dict2']

Extract dictionary values from nested keys, leaving main key, then turn into list

a = {
1: {'abc': 50, 'def': 33, 'xyz': 40},
2: {'abc': 30, 'def': 22, 'xyz': 45},
3: {'abc': 15, 'def': 11, 'xyz': 50}
}
I would like to iterate through this nested dictionary, remove the sub keys (or extract the subkey values), but keep the main keys. the second step would be to turn the dictionary into a list of lists:
b = [
[1, 50, 33, 40],
[2, 30, 22, 45],
[3, 15, 11, 50]
]
I looked through the myriad of posts here talking about extracting keys and values but cannot find a close enough example to fit to what I need (still new at this): So far, I have this:
for key in a.keys():
if type(a[key]) == dict:
a[key] = a[key].popitem()[1]
which gives this - the value of the third sub key in each key: It's a start, but not complete or what I want
{1: 40, 2: 45, 3: 50}
Use a list comprehension over a.items(), use dict.values() to get the values, and then you can use unpacking (*) to get the desired lists.
>>> [[k, *v.values()] for k,v in a.items()]
[[1, 50, 33, 40], [2, 30, 22, 45], [3, 15, 11, 50]]
This solution may be not the most elegant solution, but it does what you exactly want:
a = {
1: {'abc': 50, 'def': 33, 'xyz': 40},
2: {'abc': 30, 'def': 22, 'xyz': 45},
3: {'abc': 15, 'def': 11, 'xyz': 50}
}
b = []
for key1, dict2 in a.items():
c = [key1]
c.extend(dict2.values())
b.append(c)
print(b)

Parse list into dictionaries within a dictionary Python

dataset:
id = [1,2,3]
header = ['name','attack','defense']
stats = [['John',12,30], ['Amy',32,89], ['Lisa',45,21]]
I would like to obtain an output in the form of a nested dictionary. The keys of the outer dictionary will be the id and the values will be dictionaries the contain the other data. i.e.:
dict = {
1: {'name': 'John', 'attack': 12, 'defense': 30},
2: {'name': 'Amy', 'attack': 32, 'defense': 89},
3: {'name': 'Lisa', 'attack': 45, 'defense': 21}
}
this is my current code:
dict = {}
for i in id:
next_input = {}
for index, h in enumerate (header):
for sublist in stats:
next_input[h] = sublist[index]
dict[i] = next_input
It is not working because of the last for loop. the value of the inner dictionaries are just replacing themselves until the last sublist.
How can I correct this code?
You don't need to loop over the stats sublists; using the enumerate() option you picked, you'd have to add an index to the id loop and pick the right stats:
dict = {}
for id_index, i in enumerate(id):
next_input = {}
for h in enumerate (header):
next_input[h] = sublist[id_index][index]
dict[i] = next_input
However, you can use the zip() function to pair up two lists for parallel iteration:
result = {i: dict(zip(header, stat)) for i, stat in zip(id, stats)}
This uses a dictionary comprehension to build the outer mapping from id value to corresponding stats entry. The inner dictionary is simply build from the paired headers and statistics (dict() takes a sequence of (key, value) pairs).
Demo:
>>> id = [1,2,3]
>>> header = ['name','attack','defense']
>>> stats = [['John',12,30], ['Amy',32,89], ['Lisa',45,21]]
>>> {i: dict(zip(header, stat)) for i, stat in zip(id, stats)}
{1: {'attack': 12, 'defense': 30, 'name': 'John'}, 2: {'attack': 32, 'defense': 89, 'name': 'Amy'}, 3: {'attack': 45, 'defense': 21, 'name': 'Lisa'}}
>>> from pprint import pprint
>>> pprint(_)
{1: {'attack': 12, 'defense': 30, 'name': 'John'},
2: {'attack': 32, 'defense': 89, 'name': 'Amy'},
3: {'attack': 45, 'defense': 21, 'name': 'Lisa'}}
You can try this:
id = [1,2,3]
header = ['name','attack','defense']
stats = [['John',12,30], ['Amy',32,89], ['Lisa',45,21]]
new_dict = {a:{d:c for c, d in zip(b, header)} for a, b in zip(id, stats)}
Output:
{1: {'attack': 12, 'defense': 30, 'name': 'John'}, 2: {'attack': 32, 'defense': 89, 'name': 'Amy'}, 3: {'attack': 45, 'defense': 21, 'name': 'Lisa'}}
Another zip() variation:
d = {}
for i,s in enumerate(stats):
d[id[i]] = dict((zip(header, s)))
print(d)
The output:
{1: {'attack': 12, 'name': 'John', 'defense': 30}, 2: {'attack': 32, 'name': 'Amy', 'defense': 89}, 3: {'attack': 45, 'name': 'Lisa', 'defense': 21}}
use zip() and list comphersion
>> dict(zip(id ,[dict(zip(header,item)) for item in stats]))
{1: {'attack': 12, 'defense': 30, 'name': 'John'}, 2: {'attack': 32, 'defense': 89, 'name': 'Amy'}, 3: {'attack': 45, 'defense': 21, 'name': 'Lisa'}}
first zip every item in stats with header
>>> [dict(zip(header,item)) for item in stats]
[{'attack': 12, 'defense': 30, 'name': 'John'}, {'attack': 32, 'defense': 89, 'name': 'Amy'}, {'attack': 45, 'defense': 21, 'name': 'Lisa'}]
second zip id with the output of first
>>> zip(id,[dict(zip(header,item)) for item in stats])
[(1, {'attack': 12, 'defense': 30, 'name': 'John'}), (2, {'attack': 32, 'defense': 89, 'name': 'Amy'}), (3, {'attack': 45, 'defense': 21, 'name': 'Lisa'})]

Python dictionary: Add to the key sum of values fulfilling given condition

I've following nested dictionary, where the first number is resource ID (the total number of IDs is greater than 100 000):
dict = {1: {'age':1,'cost':14,'score':0.3},
2: {'age':1,'cost':9,'score':0.5},
...}
I want to add to each resource a sum of costs of resources with lower score than given resource. I can add 'sum_cost' key which is equal to 0 by following code:
for id in adic:
dict[id]['sum_cost'] = 0
It gives me following:
dict = {1: {'age':1,'cost':14,'score':0.3, 'sum_cost':0},
2: {'age':1,'cost':9,'score':0.5,'sum_cost':0},
...}
Now I would like to use ideally for loop (to make the code easily readable) to assign to each sum_cost a value equal of sum of cost of IDs with lower score than the given ID.
Ideal output looks like dictionary where 'sum_cost' of each ID is equal to the cost of IDs with lower score than given ID:
dict = {1: {'age':1,'cost':14,'score':0.3, 'sum_cost':0},
2: {'age':1,'cost':9,'score':0.5,'sum_cost':21},
3: {'age':13,'cost':7,'score':0.4,'sum_cost':14}}
Is there any way how to do it?
Notes:
Using sorted method for sorting the dictionary output corresponding to the key score
dictionary get method to get dictionary values
and using a temporary variable for cumulative addition os sum_cost
Code:
dicts = {1: {'age': 1, 'cost': 14, 'score': 0.3, 'sum_cost': 0},
2: {'age': 1, 'cost': 9, 'score': 0.5, 'sum_cost': 0},
3: {'age': 13, 'cost': 7, 'score': 0.4, 'sum_cost': 0}}
sum_addition = 0
for key, values in sorted(dicts.items(), key=lambda x: x[1].get('score', None)):
if dicts[key].get('score') is not None: #By default gives None when key is not available
dicts[key]['sum_cost'] = sum_addition
sum_addition += dicts[key]['cost']
print key, dicts[key]
A even more simplified method by #BernarditoLuis and #Kevin Guan advise
Code2:
dicts = {1: {'age': 1, 'cost': 14, 'score': 0.3, 'sum_cost': 0},
2: {'age': 1, 'cost': 9, 'score': 0.5, 'sum_cost': 0},
3: {'age': 13, 'cost': 7, 'score': 0.4, 'sum_cost': 0}}
sum_addition = 0
for key, values in sorted(dicts.items(), key=lambda x: x[1].get('score', None)):
if dicts[key].get('score'): #By default gives None when key is not available
dicts[key]['sum_cost'] = sum_addition
sum_addition += dicts[key]['cost']
print key, dicts[key]
Output:
1 {'sum_cost': 0, 'age': 1, 'cost': 14, 'score': 0.3}
3 {'sum_cost': 14, 'age': 13, 'cost': 7, 'score': 0.4}
2 {'sum_cost': 21, 'age': 1, 'cost': 9, 'score': 0.5}
What about using OrderedDict?
from collections import OrderedDict
origin_dict = {
1: {'age':1,'cost':14,'score':0.3},
2: {'age':1,'cost':9,'score':0.5},
3: {'age':1,'cost':8,'score':0.45}
}
# sort by score
sorted_dict = OrderedDict(sorted(origin_dict.items(), key=lambda x: x[1]['score']))
# now all you have to do is to count sum_cost successively starting from 0
sum_cost = 0
for key, value in sorted_dict.items():
value['sum_cost'] = sum_cost
sum_cost += value['cost']
print sorted_dict

sum value of two different dictionaries which is having same key

i am having two dictionaries
first = {'id': 1, 'age': 23}
second = {'id': 4, 'out': 100}
I want output dictionary as
{'id': 5, 'age': 23, 'out':100}
I tried
>>> dict(first.items() + second.items())
{'age': 23, 'id': 4, 'out': 100}
but i am getting id as 4 but i want to it to be 5 .
You want to use collections.Counter:
from collections import Counter
first = Counter({'id': 1, 'age': 23})
second = Counter({'id': 4, 'out': 100})
first_plus_second = first + second
print first_plus_second
Output:
Counter({'out': 100, 'age': 23, 'id': 5})
And if you need the result as a true dict, just use dict(first_plus_second):
>>> print dict(first_plus_second)
{'age': 23, 'id': 5, 'out': 100}
If you want to add values from the second to the first, you can do it like this:
first = {'id': 1, 'age': 23}
second = {'id': 4, 'out': 100}
for k in second:
if k in first:
first[k] += second[k]
else:
first[k] = second[k]
print first
The above will output:
{'age': 23, 'id': 5, 'out': 100}
You can simply update the 'id' key afterwards:
result = dict(first.items() + second.items())
result['id'] = first['id'] + second['id']

Categories