I have a list of Dicts as follows
[{"Sender":"bob","Receiver":"alice","Amount":50},{"Sender":"bob","Receiver":"alice","Amount":60},{"Sender":"bob","Receiver":"alice","Amount":70},{"Sender":"joe","Receiver":"bob","Amount":50},{"Sender":"joe","Receiver":"bob","Amount":150},{"Sender":"alice","Receiver":"bob","Amount":100},{"Sender":"bob","Receiver":"kyle","Amount":260}]
What i need is to sum up the totals per each unique sender/receiver pair, as well as how many total "transactions" there were per pair, as shown below in my desired output
[{"Sender":"bob","Receiver":"alice","Total":180,"Count":3},{"Sender":"joe","Receiver":"bob","Total":"200","Count":2},{"Sender":"alice","Receiver":"bob","Total":"100","Count":1}, {"Sender":"bob","Receiver":"kyle","Total":260,"Count":1}]
What i'm currently doing to get the "total" is
total = sum(a['Amount'] for a in transactions).
But this simply sums up all of the amounts across all pairs, i need the total for each unique pair of sender/receiver i would't know where to begin getting the "count" numbers, either.
CodePudding user response:
Make a new dictionary where the key is the sender/receiver pair.
Iterate over the list of senders/receivers. If that sender/receiver pair does not exist in the new dict, create it. Otherwise increment the count for that pair by one.
newdict = {}
for row in transactions:
sender = row['Sender']
receiver = row['Receiver']
amount = row['Amount']
key = f'{sender},{receiver}'
if key in newdict:
newdict[key]['Total'] = amount
newdict[key]['Count'] = 1
else:
newdict[key] = {'Count': 1, 'Total': amount}
This solution produces a single dict instead of a list of dicts.
CodePudding user response:
lookup for existing value and keep updating it. e.g.
transactions = [
{"Sender":"bob","Receiver":"alice","Amount":50},
{"Sender":"bob","Receiver":"alice","Amount":60},
{"Sender":"bob","Receiver":"alice","Amount":70},
{"Sender":"joe","Receiver":"bob","Amount":50},
{"Sender":"joe","Receiver":"bob","Amount":150},
{"Sender":"alice","Receiver":"bob","Amount":100},
{"Sender":"bob","Receiver":"kyle","Amount":260}
]
output = []
def find_transaction(sender: str, receiver: str):
for o in output:
if o["Sender"] == sender and o["Receiver"] == receiver:
return o
return None
for tx in transactions:
existing = find_transaction(tx["Sender"], tx["Receiver"])
if existing:
existing["Total"] = tx["Amount"]
existing["Count"] = 1
else:
output.append({
"Sender": tx["Sender"],
"Receiver": tx["Receiver"],
"Total": tx["Amount"],
"Count": 1
})
print(output)
CodePudding user response:
- Use a
dict
to group the amounts, using(sender, receiver)
as key; - Rebuild your list of dicts from the dict of amounts.
ll = [{"Sender":"bob","Receiver":"alice","Amount":50},{"Sender":"bob","Receiver":"alice","Amount":60},{"Sender":"bob","Receiver":"alice","Amount":70},{"Sender":"joe","Receiver":"bob","Amount":50},{"Sender":"joe","Receiver":"bob","Amount":150},{"Sender":"alice","Receiver":"bob","Amount":100},{"Sender":"bob","Receiver":"kyle","Amount":260}]
# GROUP BY (SENDER, RECEIVER) AND REDUCE BY KEY
d = {}
for transaction in ll:
k = (transaction['Sender'], transaction['Receiver'])
d[k] = d.get(k, 0) transaction['Amount']
# print(d)
# d = {('bob', 'alice'): 180, ('joe', 'bob'): 200, ('alice', 'bob'): 100, ('bob', 'kyle'): 260}
# REBUILD LIST OF DICT
new_ll = [{'Sender': s, 'Receiver': r, 'Amount': a} for (s,r),a in d.items()]
print(new_ll)
# [{'Sender': 'bob', 'Receiver': 'alice', 'Amount': 180}, {'Sender': 'joe', 'Receiver': 'bob', 'Amount': 200}, {'Sender': 'alice', 'Receiver': 'bob', 'Amount': 100}, {'Sender': 'bob', 'Receiver': 'kyle', 'Amount': 260}]
These kinds of group-by-key and reduce-by-key operations are extremely common. Using a dict to group by key is the best method. There is also a library function in module more_itertools
:
from more_itertools import map_reduce
from operator import itemgetter
ll = ll = [{"Sender":"bob","Receiver":"alice","Amount":50},{"Sender":"bob","Receiver":"alice","Amount":60},{"Sender":"bob","Receiver":"alice","Amount":70},{"Sender":"joe","Receiver":"bob","Amount":50},{"Sender":"joe","Receiver":"bob","Amount":150},{"Sender":"alice","Receiver":"bob","Amount":100},{"Sender":"bob","Receiver":"kyle","Amount":260}]
d = map_reduce(ll, keyfunc=itemgetter('Sender', 'Receiver'), valuefunc=itemgetter('Amount'), reducefunc=sum)
print(d)
# defaultdict(None, {('bob', 'alice'): 180, ('joe', 'bob'): 200, ('alice', 'bob'): 100, ('bob', 'kyle'): 260})
new_ll = [{'Sender': s, 'Receiver': r, 'Amount': a} for (s,r),a in d.items()]