I have two lists of dictionaries:
# shop_qs
[{'month': 'Jul', 'sales': 1, 'revenue': Decimal('180')}, {'month': 'Aug', 'sales': 2, 'revenue': Decimal('525')}]
# com_qs
[{'month': 'Jun', 'sales': 1, 'revenue': Decimal('200')}, {'month': 'Aug', 'sales': 1, 'revenue': Decimal('450')}]
I am attempting to merge the dictionaries in the two lists by month and then add the values in the sales fields and the values in the revenue fields together, for that month.
This is what I have attempted to far but, as you can see, yields a result no where near what is desired.
total_chained_ls = list(chain(shop_qs, com_qs))
total_ls = defaultdict(int)
for d in total_chained_ls:
for k, v in d.items():
if isinstance(v, int) or isinstance(v, decimal.Decimal):
total_ls[k] = total_ls[k] v
else:
total_ls[k] = v
# defaultdict(<class 'int'>, {'month': 'Aug', 'sales': 5, 'revenue': Decimal('1355')})
This is the desired output:
[{'month': 'Jun', 'sales': 1, 'revenue': Decimal('200')}, {'month': 'Jul', 'sales': 1, 'revenue': Decimal('180')}, {'month': 'Aug', 'sales': 3, 'revenue': Decimal('975')}]
How would you go about doing this in Python?
Note: the Decimal
values in the fields acts like a normal int.
CodePudding user response:
See if this could help you:
from decimal import *
# shop_qs
lst_1 = [{'month': 'Jul', 'sales': 1, 'revenue': Decimal('180')}, {'month': 'Aug', 'sales': 2, 'revenue': Decimal('525')}]
# com_qs
lst_2 = [{'month': 'Jun', 'sales': 1, 'revenue': Decimal('200')}, {'month': 'Aug', 'sales': 1, 'revenue': Decimal('450')}]
# ------------------------------------------------------------------------------------------------------------------------
# merge list_2 into list_1
for x in lst_2:
lst_1.append(x)
# define months of year
months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
# loop months to do the job
lst = []
lst_done = []
for month in months:
dct = {}
for d in lst_1:
# check the month to decide if it should be processed or not
if d['month'] == month and month not in lst_done:
sales = 0
revenue = 0
# calculate sums
if month not in dct.keys():
sales_list = [ lst_1[j]['sales'] for j in range(len(lst_1)) if lst_1[j]['month'] == month ]
for x in sales_list:
sales = x
revenue_list = [ lst_1[j]['revenue'] for j in range(len(lst_1)) if lst_1[j]['month'] == month ]
for x in revenue_list:
revenue = x
# fill in the dictionary for the month and append it to lst
dct['month'] = month
dct['sales'] = sales
dct['revenue'] = revenue
lst.append(dct)
# putting the month into the done list
lst_done.append(month)
''' R e s u l t i n g l i s t: (lst)
[
{'month': 'Jun', 'sales': 1, 'revenue': Decimal('200')},
{'month': 'Jul', 'sales': 1, 'revenue': Decimal('180')},
{'month': 'Aug', 'sales': 3, 'revenue': Decimal('975')}
]
'''
CodePudding user response:
You correctly applied chain()
to merge lists, but you shouldn't consume returned iterator into a list as it turns whole expression into shop_qs com_qs
. As you have static keys where you need to sum values, you don't need to initialize inner loop to iterate over pairs in each dictionary but just make sum manually. With certain edits your code could be turned into this:
from decimal import Decimal
from itertools import chain
shop_qs = [
{'month': 'Jul', 'sales': 1, 'revenue': Decimal('180')},
{'month': 'Aug', 'sales': 2, 'revenue': Decimal('525')}
]
com_qs = [
{'month': 'Jun', 'sales': 1, 'revenue': Decimal('200')},
{'month': 'Aug', 'sales': 1, 'revenue': Decimal('450')}
]
total_ls = {}
for d in chain(shop_qs, com_qs):
month = d.pop('month')
item = total_ls.get(month)
if item:
item['sales'] = d['sales']
item['revenue'] = d['revenue']
else:
total_ls[month] = d
Important notice: dict.pop()
will modify items of both shop_qs
and com_qs
.
After execution your total_ls
will have data in next format:
{
'Jul': {
'sales': 1,
'revenue': Decimal('180')
},
'Aug': {
'sales': 3,
'revenue': Decimal('975')
},
'Jun': {
'sales': 1,
'revenue': Decimal('200')
}
}
You can turn it back into list of dictionaries using next list comprehension:
[{'month': k} | v for k, v in total_ls.items()]
# OR (for older python versions)
[{'month': k, **v} for k, v in total_ls.items()]
Another way to merge items by certain criteria is to use groupby()
on sorted data.
from decimal import Decimal
from itertools import chain, groupby
from operator import itemgetter
shop_qs = [
{'month': 'Jul', 'sales': 1, 'revenue': Decimal('180')},
{'month': 'Aug', 'sales': 2, 'revenue': Decimal('525')}
]
com_qs = [
{'month': 'Jun', 'sales': 1, 'revenue': Decimal('200')},
{'month': 'Aug', 'sales': 1, 'revenue': Decimal('450')}
]
month_getter = itemgetter('month')
sorted_chained_ls = sorted(chain(shop_qs, com_qs), key=month_getter)
total_ls = []
for _, group in groupby(sorted_chained_ls, month_getter):
item = next(group)
total_ls.append(item)
for i in group:
item['sales'] = i['sales']
item['revenue'] = i['revenue']
You can help my country, check my profile info.