Home > Mobile >  Merging two lists of dictionaries with shared keys throughout the dictionaries
Merging two lists of dictionaries with shared keys throughout the dictionaries

Time:08-28

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.

  • Related