UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

SOLVED: How to calculate yearly earnings with a regular, yearly pay rate increase

Forums > Swift

I'm using macOS Command Line Tool to work out the formulae before transitioning to an app. The current short-term goal is to print an employee's yearly earnings based on hours worked per month and an array of increasing pay rates correlating with the employee's years working for the company. Element #1 will be the pay rate for year 1, #2 for year 2, etc. My current code only calculates annual earnings using the tenth element because I have used

EmploymentYears.count

. Consequently the print shows the same amount of annual earnings for every year: $72,960 (160 hours x 12 months x 38 hours worked). I'm not sure how to make this dynamic. Any ideas?


import Foundation

let pennyRoundingBehavior = NSDecimalNumberHandler(
    roundingMode: .bankers,
    scale: 2,
    raiseOnExactness: false,
    raiseOnOverflow: true,
    raiseOnUnderflow: true,
    raiseOnDivideByZero: true
)

func roundToNearestPenny(rate: Decimal, of dollarAmount: Decimal) -> Decimal
{
    assert((0...100).contains(rate))

    let x = ((dollarAmount * rate)  as NSDecimalNumber)
    return x.rounding(accordingToBehavior: pennyRoundingBehavior) as Decimal
}

enum EmploymentYears: Int, CaseIterable
{
 case Year1, Year2, Year3, Year4, Year5, Year6, Year7, Year8, Year9, Year10

    static var count:Int  { allCases.count }
}

struct YearlyEarnings
{
    var yearlyEarnings  : Decimal = 0

    init(monthlyHoursWorked: Decimal, payRate: Decimal)
    {
        let earnings = roundToNearestPenny(
            rate: payRate,
            of: monthlyHoursWorked
        )
        self.yearlyEarnings = earnings * 12
    }
}

extension Array where Element == YearlyEarnings {
    subscript (index: EmploymentYears) -> Element { self[index.rawValue] }
}

func computeYearlyEarnings(
    monthlyHoursWorked   : Decimal,
    rate                 : [Decimal]) -> [YearlyEarnings]
{
    var yearlyEarnings: [YearlyEarnings] = []
    yearlyEarnings.reserveCapacity(EmploymentYears.count)

    let x = EmploymentYears.count

    let yearlyEarning = roundToNearestPenny(
        rate: rate[x],
        of: monthlyHoursWorked
    )

    return yearlyEarnings
}

var monthlyHoursWorked   : Decimal = 160.0
var rate                 : [Decimal] = [0,20,22,24,26,28,30,32,34,36,38,40]

let yearlyEarnings = YearlyEarnings(
    monthlyHoursWorked: monthlyHoursWorked,
    payRate: rate[EmploymentYears.count]
)

for year in EmploymentYears.allCases
{
    let curEarning = yearlyEarnings
    print("Earnings for \(year)")
    print("    $\(curEarning.yearlyEarnings)")
    print()
}

2      

Something like this, perhaps?

import Foundation

//I pulled all the calculation stuff into an enum with no cases.
//With no cases, this enum can't be instantiated.
//This is a way to simulate namespaces in Swift.
enum Calculations {
    static func payRate(forYearsWorked numberOfYears: Int) -> Int {
        let rates = [20,22,24,26,28,30,32,34,36,38,40]
        guard numberOfYears <= rates.count else {
            fatalError("numberOfYears exceeds rates.count")
        }

        return rates[numberOfYears - 1]
    }

    private static let pennyRoundingBehavior = NSDecimalNumberHandler(
        roundingMode: .bankers,
        scale: 2,
        raiseOnExactness: false,
        raiseOnOverflow: true,
        raiseOnUnderflow: true,
        raiseOnDivideByZero: true
    )

    static func computeMonthlyEarnings(hours hoursWorked: Decimal,
                                       atRate payRate: Decimal) -> Decimal {
        let base = (hoursWorked * payRate) as NSDecimalNumber
        return base.rounding(accordingToBehavior: pennyRoundingBehavior) as Decimal
    }

    static func computeYearlyEarnings(monthlyHours: Int,
                                      yearsWorked: Int) -> [YearlyEarnings] {
        var yearlyEarnings: [YearlyEarnings] = []

        for yr in 1...yearsWorked {
            yearlyEarnings.append(
                YearlyEarnings(
                    monthlyHoursWorked: Decimal(monthlyHours),
                    atRate: payRate(forYearsWorked: yr))
            )
        }

        return yearlyEarnings
    }

}

struct YearlyEarnings
{
    var amount: Decimal

    init(monthlyHoursWorked: Decimal, atRate payRate: Int)
    {
        let earnings = Calculations.computeMonthlyEarnings(
            hours: monthlyHoursWorked,
            atRate: Decimal(payRate)
        )
        self.amount = earnings * 12
    }
}

var monthlyHoursWorked: Int = 160

let yearlyEarnings = Calculations.computeYearlyEarnings(
    monthlyHours: monthlyHoursWorked,
    yearsWorked: 11)

for idx in 0..<yearlyEarnings.count {
    print("Earnings for year \(idx + 1)")
    print("    $\(yearlyEarnings[idx].amount)")
    print()
}

I have to confess to being a bit confused trying to figure out your original code. I hope I've got the right idea what you are going for. Here's the output:

Earnings for year 1
    $38400

Earnings for year 2
    $42240

Earnings for year 3
    $46080

Earnings for year 4
    $49920

Earnings for year 5
    $53760

Earnings for year 6
    $57600

Earnings for year 7
    $61440

Earnings for year 8
    $65280

Earnings for year 9
    $69120

Earnings for year 10
    $72960

2      

@roosterboy That's perfect! I'm super new to coding which may explain the chaotic nature of my original code. Thank you for your help! If $40/hr is the last pay rate that applies to all years of employment after reaching that year (11th year of employment), is there a way to make $40/hr the default? Perhaps replace the fatal error with "40"?

2      

Yep, just change payRate(forYearsWorked:) to this:

static func payRate(forYearsWorked numberOfYears: Int) -> Int {
    let rates = [20,22,24,26,28,30,32,34,36,38,40]
    if numberOfYears > rates.count {
        return rates.last!
    } else {
        return rates[numberOfYears - 1]
    }
}

3      

Hacking with Swift is sponsored by RevenueCat

SPONSORED Take the pain out of configuring and testing your paywalls. RevenueCat's Paywalls allow you to remotely configure your entire paywall view without any code changes or app updates.

Learn more here

Sponsor Hacking with Swift and reach the world's largest Swift community!

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.