简体   繁体   中英

merge values of same key in a list of dictionaries , and compare to another list of dictionaries in python3

Update: Apparently, I noticed that in my main code, when I extract the values from the list of dictionaries that I get from readExpenses.py, I store it as a set, not as a list of dictionaries.

Now, I know that I store each dictionary in the 'exp' list with these lines of code:

for e in expenses:

    exp.append(e)

However, I only want the Keys Amount, and Type from those dictionaries, and not the other entries.

For reference, here is the list of keys in an expense dictionary:

"Date","Description","Type","Check Number","Amount","Balance"

As mentioned before, I only need Type and Amount.

I am trying to make a budget program, So I have this list of dictionaries:

[{'Bills': 30.0}, {'Bills': 101.53}, {'Bills': 60.0}, {'Bills': 52.45}, {'Gas': 51.17}, {500.0: 'Mortgage'}, {'Food': 5.1}]

And I'm trying to compare it to this list of dictionaries:

[{400.0: 'Bills'}, {'Gas': 100.0}, {500.0: 'Mortgage'}, {'Food': 45.0}]

The first list is how much money I spent on different services in a given month, and what category it was in, and the second dictionary is the max amount that the budget allows me to spend on said category.

The goal is, in the first dictionary, to combine all the values of the same key into one key:value pair, then compare it to the second dictionary.

So I should get this list of dictionaries out of the first one:

[{'Bills': 295.15), {'Gas': 51.17}, {500.0: 'Mortgage'}, {'Food': 5.1}]

I tried looking at this example and this one , but they are just about merging the dictionaries lists together, and not summing the values of the same key. I did try the code in the latter, but it only joined the dictionaries together. I noticed that sum only seems to work with "raw" dictionaries, and not with lists of dictionaries.

I did try this as a thought experiment:

print(sum(item['amount'] for item in exp))

I know that would sum up all the numbers under amount, rather than return a number for each category, but I wanted to try out it for the heck of it, to see if it would lead to a solution, but I got this error in return:

TypeError: 'set' object is not subscriptable

The Counter function seemed to show promise as a solution as well when I was messing around, however, it seems to only work with dictionaries that are on their own, and not with list of dictionaries.

#where exp is the first dictionary that I mentioned
a = Counter(exp)
b = Counter(exp) 

c = a + b #I'm aware the math would have be faulty on this, but this was a test run
print (c)

This attempt returned this error:

TypeError: unhashable type: 'set'

Also, is there a way to do it without importing the collections module and using what comes with python as well?

My code:

from readExpense import *
from budget import *
from collections import *

#Returns the expenses by expenses type
def expensesByType(expenses, budget):

    exp = []

    expByType = []

    bud = []


    for e in expenses:

        entry = {e['exptype'], e['amount']}

        exp.append(entry)

    for b in budget:
        entry = {b['exptype'], b['maxamnt']}

        bud.append(entry)



    return expByType;


def Main():

    budget = readBudget("budget.txt")
    #printBudget(budget)

    expenses = readExpenses("expenses.txt")
    #printExpenses(expenses)

    expByType = expensesByType(expenses, budget)        

if __name__ == '__main__':
    Main()

And for reference, the code from budget and readexpense respectively.

budget.py

def readBudget(budgetFile):
    # Read the file into list lines
    f = open(budgetFile)
    lines = f.readlines()
    f.close()

    budget = []

    # Parse the lines
    for i in range(len(lines)):
        list = lines[i].split(",")

        exptype = list[0].strip('" \n')
        if exptype == "Type":
            continue

        maxamount = list[1].strip('$" \n\r')

        entry = {'exptype':exptype, 'maxamnt':float(maxamount)}

        budget.append(entry)

    return budget

def printBudget(budget):
    print()
    print("================= BUDGET ==================")
    print("Type".ljust(12), "Max Amount".ljust(12))

    total = 0
    for b in budget:
        print(b['exptype'].ljust(12), str("$%0.2f" %b['maxamnt']).ljust(50))
        total = total + b['maxamnt']

    print("Total: ", "$%0.2f" % total)

def Main():
    budget = readBudget("budget.txt")
    printBudget(budget)

if __name__ == '__main__':    
    Main()

readExpense.py

def readExpenses(file):

    #read file into list of lines
    #split lines into fields
    # for each list create a dictionary
    # add dictionary to expense list

    #return expenses in a list of dictionary with fields
    # date desc, exptype checknm, amnt

    f = open(file)
    lines=f.readlines()
    f.close()

    expenses = []

    for i in range(len(lines)):
        list = lines[i].split(",")

        date = list[0].strip('" \n')
        if date == "Date":
            continue

        description = list[1].strip('" \n\r')
        exptype= list[2].strip('" \n\r')
        checkNum = list[3].strip('" \n\r')
        amount = list[4].strip('($)" \n\r')
        balance = list[5].strip('" \n\r')

        entry  ={'date':date, 'description': description, 'exptype':exptype, 'checkNum':checkNum, 'amount':float(amount), 'balance': balance}

        expenses.append(entry)

    return expenses

def printExpenses(expenses):

    #print expenses
    print()
    print("================= Expenses ==================")
    print("Date".ljust(12), "Description".ljust(12), "Type".ljust(12),"Check Number".ljust(12), "Amount".ljust(12), "Balance".ljust(12))

    total = 0

    for e in expenses:
        print(str(e['date']).ljust(12), str(e['description']).ljust(12), str(e['exptype']).ljust(12), str(e['checkNum']).ljust(12), str(e['amount']).ljust(12))
        total = total + e['amount']


    print()

    print("Total: ", "$%0.2f" % total)

def Main():
    expenses = readExpenses("expenses.txt")
    printExpenses(expenses)

if __name__ == '__main__':
    Main()

Is there a reason you're avoiding creating some objects to manage this? If it were me, I'd go objects and do something like the following (this is completely untested, there may be typos):

#!/usr/bin/env python3

from datetime import datetime # why python guys, do you make me write code like this??
from operator import itemgetter

class BudgetCategory(object):
    def __init__(self, name, allowance):
        super().__init__()
            self.name = name # string naming this category, e.g. 'Food'
            self.allowance = allowance # e.g. 400.00 this month for Food
            self.expenditures = [] # initially empty list of expenditures you've made

    def spend(self, amount, when=None, description=None):
        ''' Use this to add expenditures to your budget category'''
        timeOfExpenditure = datetime.utcnow() if when is None else when #optional argument for time of expenditure
        record = (amount, timeOfExpenditure, '' if description is None else description) # a named tuple would be better here...
        self.expenditures.append(record) # add to list of expenditures
        self.expenditures.sort(key=itemgetter(1)) # keep them sorted by date for the fun of it

    # Very tempting to the turn both of the following into @property decorated functions, but let's swallow only so much today, huh?
    def totalSpent(self):
        return sum(t[0] for t in self.expenditures)

    def balance(self):
        return self.allowance - self.totalSpent()

Now I can right code that looks like:

budget = BudgetCategory(name='Food', allowance=200)
budget.spend(5)
budget.spend(8)

print('total spent:', budget.totalSpent())
print('left to go:', budget.balance())

This is just a starting point. Now you can you add methods that group (and sum) the expenditures list by decoration (eg "I spent HOW MUCH on Twinkies last month???"). You can add a method that parses entries from a file, or emits them to a csv list. You can do some charting based on time.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM