Skip to content

Instantly share code, notes, and snippets.

@bazwilliams
Last active November 16, 2017 10:53
Show Gist options
  • Save bazwilliams/1c30692f0b0190936d80be54579f9039 to your computer and use it in GitHub Desktop.
Save bazwilliams/1c30692f0b0190936d80be54579f9039 to your computer and use it in GitHub Desktop.
Our mobbed solution to the Codor Dojo set at Python Glasgow (https://www.meetup.com/Python-Glasgow/events/244377303/)
def addUp(contribution):
return sum(contribution) / len(contribution)
def calculate_new_contribution(contribution, delta, involvement):
return contribution - delta if involvement else 0
def calculate_overpayment(contribution, involvement):
d = addUp([c for c,i in zip(contribution, involvement) if i])
return [calculate_new_contribution(c,d,i) for c,i in zip(contribution, involvement)]
def calculate_overpayments(contributions, involvements):
overpayments = [calculate_overpayment(c,i) for c, i in zip(contributions, involvements)]
return [round(sum(col),2) for col in zip(*overpayments)]
contributions =[
[ 80.61, 0, 0, 0 ],
[ 0, 0, 0, 46.86 ],
[ 0, 71.75, 71.75, 0 ],
[ 0, 0, 0, 17.64 ],
[ 0 ,0, 0, 8],
[ 0, 0, 0, 18.45],
[ 0, 6.55,0, 0]
]
involvements = [
[True, True, True, True],
[True, True, True, True],
[True, True, True, True],
[False, True, True, True],
[True, True, True, True],
[True, True, True, True],
[True, True, True, True],
]
print(calculate_overpayments(contributions, involvements))
@bazwilliams
Copy link
Author

bazwilliams commented Nov 15, 2017

Contributors:

# craig muir
# barry williams
# jonathan dorling
# john daly
# tony murray

@lguariento
Copy link

print(calculate_overpayments(contributions, involvements))

if you want to make it python3 friendly :)

@pyeater
Copy link

pyeater commented Nov 16, 2017

Cheers baz, well done especially to you and jonathan, you worked well together (pyeater - tony).

Here's my effort:

class Expenses:    

    def __init__(self):
        self.expenses = {}        

    def add_expense_entry(self, descript_key, contrib_involve_dict):
        '''
        Add expenses entry     
        '''
        self.expenses[descript_key] = contrib_involve_dict

    def grand_total(self):
        '''
        Calculates the grand total. Extra function!     
        '''
        return sum([self.sum_entry(self.expenses[x]) for x in self.expenses])

    def sum_entry(self, entry_dict):
        '''
        Calculates the total for a given expenses entry.     
        '''
        return sum([x[0] for x in entry_dict.values()])

    def calc_split_entry(self, entry_dict):
        '''
        Calculates the equal split between those involved i.e True in table.     
        '''
        divisor = sum([1 if x[1] is True else 0 for x in entry_dict.values()])
        return self.sum_entry(entry_dict)/divisor

    def sum_paid_person(self, person):
        '''
        Calculates the total sum paid by a person overall.     
        '''
        return sum([x[person][0] for x in self.expenses.values()])

    def sum_commit_person(self, person):
        '''
        Calculates the total sum for expenses incurred where involved.     
        '''
        return sum([self.calc_split_entry(x) for x in self.expenses.values() if x[person][1] is True])

    def overpayment_person(self, person):
        '''
        Calculates the overpayment(+) owed or underpayment(-) to make.     
        '''
        return self.sum_paid_person(person) - self.sum_commit_person(person)


# Init and add expense entries
expenses = Expenses()
expenses.add_expense_entry('Tesco_1', {'Clara':[80.61,True], 'Gordon':[0.0,True], 'Nina':[0.0,True], 'Eunice':[0.0,True]})
expenses.add_expense_entry('Tesco_2', {'Clara':[0.0,True], 'Gordon':[0.0,True], 'Nina':[0.0,True], 'Eunice':[46.86,True]})
expenses.add_expense_entry('Wild_Duck', {'Clara':[0.0,True], 'Gordon':[71.75,True], 'Nina':[71.75,True], 'Eunice':[0.0,True]})
expenses.add_expense_entry('Meat_BBQ', {'Clara':[0.0,False], 'Gordon':[0.0,True], 'Nina':[0.0,True], 'Eunice':[17.64,True]})
expenses.add_expense_entry('Misc_BBQ', {'Clara':[0.0,True], 'Gordon':[0.0,True], 'Nina':[0.0,True], 'Eunice':[8.0,True]})
expenses.add_expense_entry('Red_Lion_1', {'Clara':[0.0,True], 'Gordon':[0.0,True], 'Nina':[0.0,True], 'Eunice':[18.45,True]})
expenses.add_expense_entry('Red_lion_2', {'Clara':[0.0,True], 'Gordon':[6.55,True], 'Nina':[0.0,True], 'Eunice':[0.0,True]})

# Helper Functions
r2 = lambda x: round(x, 2)

# Print Results
persons = ['Clara', 'Gordon', 'Nina', 'Eunice']
plate = '{:<10}{:>8}{:>8}{:>8}'
print(plate.format('_____', '_____', '_____', '_____'))
print(plate.format('Person', 'Paid', 'Commit', 'Over'))
print(plate.format('_____', '_____', '_____', '_____'))            
for p in persons:
    print(plate.format(p, r2(expenses.sum_paid_person(p)), r2(expenses.sum_commit_person(p)),
                       r2(expenses.overpayment_person(p))))

# Output:
_____        _____   _____   _____
Person        Paid  Commit    Over
_____        _____   _____   _____
Clara        80.61   75.99    4.62
Gordon        78.3   81.87   -3.57
Nina         71.75   81.87  -10.12
Eunice       90.95   81.87    9.08

@bazwilliams
Copy link
Author

Thanks @lguariento, I've updated the gist:)

@pyeater - I like that solution!

@pyeater
Copy link

pyeater commented Nov 16, 2017

Modified add_expense_entry() method to make data entry a little less tedious. I assume that using a for loop is ok for the data entry task.
Works for case where item is non-default and both float and bool elements are given, e.g [5.88,False], which would be an odd circumstance, paying for an expense in which you had no involvement.

def add_expense_entry(self, descript_key, contrib_involve_dict):
        '''
        Add expenses entry
        Modified using for loop and default entry to make data entry less tedious.
        '''
        default_item = [0.0,True]
        for person, item in contrib_involve_dict.items():
            if len(item) == 0:
                contrib_involve_dict[person] = default_item
            elif len(item) == 1:
                type_item = type(item[0])
                if type_item == bool:
                    contrib_involve_dict[person] = [default_item[0], item[0]]
                elif type_item == float:
                    contrib_involve_dict[person] = [item[0], default_item[1]]             
                    
        self.expenses[descript_key] = contrib_involve_dict

# Init and add expense entries
expenses = Expenses()
expenses.add_expense_entry('Tesco_1', {'Clara':[80.61], 'Gordon':[], 'Nina':[], 'Eunice':[]})
expenses.add_expense_entry('Tesco_2', {'Clara':[], 'Gordon':[], 'Nina':[], 'Eunice':[46.86]})
expenses.add_expense_entry('Wild_Duck', {'Clara':[], 'Gordon':[71.75], 'Nina':[71.75], 'Eunice':[]})
expenses.add_expense_entry('Meat_BBQ', {'Clara':[False], 'Gordon':[], 'Nina':[], 'Eunice':[17.64]})
expenses.add_expense_entry('Misc_BBQ', {'Clara':[], 'Gordon':[], 'Nina':[], 'Eunice':[8.0]})
expenses.add_expense_entry('Red_Lion_1', {'Clara':[], 'Gordon':[], 'Nina':[], 'Eunice':[18.45]})
expenses.add_expense_entry('Red_lion_2', {'Clara':[], 'Gordon':[6.55], 'Nina':[], 'Eunice':[]})

# Output (same)
_____        _____   _____   _____
Person        Paid  Commit    Over
_____        _____   _____   _____
Clara        80.61   75.99    4.62
Gordon        78.3   81.87   -3.57
Nina         71.75   81.87  -10.12
Eunice       90.95   81.87    9.08

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment