WWDC24 SALE: Save 50% on all my Swift books and bundles! >>

SOLVED: Using a for-in loop to project yearly 401k balances based on monthly deposits accounting for several future promotions/pay raises

Forums > Swift

Purpose of project: allow an employee to calculate/project future 401k yearly balances based on several future promotions. With promotions come pay raises.

One assumption is that 401k monthly deposits are an unchanging percentage of income.

I'm currenty working within a macOS Command Line Tool for debugging purposes. Eventually I will move this into an iOS app project.

The entire code is found on GitHub here.

However I was able to fit it all here below. Here is the first section:

enum Calculations {
    private static let pennyRoundingBehavior = NSDecimalNumberHandler(
        roundingMode: .bankers,
        scale: 2,
        raiseOnExactness: false,
        raiseOnOverflow: true,
        raiseOnUnderflow: true,
        raiseOnDivideByZero: true
    )
    static func computeMonthlyEarnings(hours hoursWorked: Int, atPayRate payRate: Int) -> Decimal {
        let base = Decimal(hoursWorked * payRate) as NSDecimalNumber
        return base.rounding(accordingToBehavior: pennyRoundingBehavior) as Decimal
    }
    static func computeMonthlySavings(hours hoursWorked: Int, atPayRate payRate: Int, percentSaved percent: Double) -> Decimal {
        let base = Decimal(hoursWorked * payRate) * Decimal(percent / 100.0) as NSDecimalNumber
        return base.rounding(accordingToBehavior: pennyRoundingBehavior) as Decimal
    }
    static func computeMonthlyRoth(hours hoursWorked: Int, atPayRate payRate: Int, percentToRoth percent: Double) -> Decimal {
        let base = Decimal(hoursWorked * payRate) * Decimal(percent / 100.0) as NSDecimalNumber
        return base.rounding(accordingToBehavior: pennyRoundingBehavior) as Decimal
    }
}

extension Array {
    func chunked(into size: Int) -> [[Element]] {
        return stride(from: 0, to: count, by: size).map {
            Array(self[$0 ..< Swift.min($0 + size, count)])
        }
    }
}

enum Department: RawRepresentable {
    case sales
    case management
    case seniorManagement
    case other(String)

    init?(rawValue: String) {
        switch rawValue {
        case "Sales": self = .sales
        case "Management": self = .management
        case "Senior Management": self = .seniorManagement
        case let dept: self = .other(dept)
        }
    }
    var rawValue: String {
        switch self {
        case .sales: return "Sales"
        case .management: return "Management"
        case .seniorManagement: return "Senior Management"
        case .other(let dept): return dept
        }
    }
    func payRate(forYearsWorked numberOfYears: Int) -> Int {
        let rates: [Int]
        switch self {
        case .sales: rates = [20,22,24,26,28,30,32,34,36,38,40]
        case .management: rates = [30,32,34,36,38,40,42,44,46,48,50]
        case .seniorManagement: rates = [40,42,44,46,48,50,52,54,56,58,60]
        default: rates = [10,12,14,16,18,20,22,24,26,28,30]
        }
        return rates[min(numberOfYears, rates.count) - 1]
    }
}

struct Employee {
    let name: String
    var birthdate: Date
    var hiredate: Date
    var retirementAge: Int
    var department: Department
    var monthlyHoursWorked: Int
    var percentToSave: Double
    var existing401kbalance: Decimal
    var percentToRoth: Double
    var percentToTrad: Double

    private(set) var employmentHistory: [Department]

    init(_ name: String,
         birthdate: Date,
         hiredate: Date,
         retirementAge: Int,
         department: Department = .other("Employee"),
         monthlyHoursWorked: Int,
         percentToSave: Double,
         existing401kbalance: Decimal,
         percentToRoth: Double,
         percentToTrad: Double)
    {
        self.name = name
        self.birthdate = birthdate
        self.hiredate = hiredate
        self.retirementAge = retirementAge
        self.department = department
        self.monthlyHoursWorked = monthlyHoursWorked
        self.percentToSave = percentToSave
        self.existing401kbalance = existing401kbalance
        self.percentToRoth = percentToRoth
        self.percentToTrad = percentToTrad
        self.employmentHistory = []
    }
}

extension Employee {
    mutating func promote(to newDepartment: Department) {
        department = newDepartment
    }
    mutating func work(months: Int) {
        employmentHistory
            .append(contentsOf: Array.init(repeating: department,
                                           count: months))
    }
    mutating func work(years: Int) {
        work(months: years * 12)
    }
    mutating func work(years: Int, months: Int) {
        work(months: years * 12 + months)
    }
    func yearlyEarnings() -> [Decimal] {
        let years = employmentHistory.chunked(into: 12)
        var earnings: [Decimal] = []
        for (index, months) in years.enumerated() {
            let yearEarnings: Decimal = months.reduce(0) { runningTotal, dept in
                let payRate = dept.payRate(forYearsWorked: index + 1)
                let monthlyEarnings = Calculations.computeMonthlyEarnings(
                    hours: monthlyHoursWorked,
                    atPayRate: payRate
                )
                return runningTotal + monthlyEarnings
            }
            earnings.append(yearEarnings)
        }
        return earnings
    }
    func yearlySavings() -> [Decimal] {
        let years = employmentHistory.chunked(into: 12)
        var savings: [Decimal] = []
        for (index, months) in years.enumerated() {
            let yearSavings: Decimal = months.reduce(0) { runningTotal, dept in
                let payRate = dept.payRate(forYearsWorked: index + 1)
                let monthlySavings = Calculations.computeMonthlySavings(
                    hours: monthlyHoursWorked,
                    atPayRate: payRate,
                    percentSaved: percentToSave
                )
                return runningTotal + monthlySavings
            }
            savings.append(yearSavings)
        }
        return savings
    }
    func calc401k(principal: Decimal, deposits: Decimal, rateOfReturn: Decimal) -> Decimal {
        //Compound interest for principal
        // P(1 + r/n)^(nt)
        //Future value of a series
        // PMT x {[(1 + r/n)^(nt) - 1] / (r/n)}
        //Key:
        // PMT = monthly payment amount
        // r = annual interest rate
        // n = numebr of times interest is compounded per year
        // t = time in years
        // ^ = ... to the power of...

        //Start with Conpound interest for principal
        // first segment: (1 + r/n)
        // n = compounded 12 times a year
        let principalBase = 1 + ((rateOfReturn/100)/12)
        // second segment: ^(nt)
        // n = compounded 12 times a year
        // t = time is 1 year
        let principalPower = pow(principalBase,12)
        // third segment: times P
        let principalInterest = principal * principalPower

        //Now Future value of the series
        //first segment: (1 + r/n)
        let rateFrequencyCompounded = 1+((rateOfReturn/100)/12)
        //second segment:
        // ^(nt) - 1
        // n = compounded 12 times a year
        // t = time is 1 year
        let toThePower = pow(rateFrequencyCompounded,12) - 1
        //third segment:
        // divided: / (r/n)
        // n = compounded 12 times a year
        let divided = toThePower / ((rateOfReturn/100)/12)
        //fourth segment:
        // mlutiplied by monthly deposit PMT
        let futureInterest = divided * deposits
        return futureInterest + principalInterest
    }

Here is the section of code that I need to perfect. I currently have three errors:

func depositsToRoth() -> [Decimal] {
        let years = employmentHistory.chunked(into: 12)
        var toRoth: [Decimal] = []
        var rothBalances: [Decimal] = []
        for (index, months) in years.enumerated() {
            let yearToRoth: Decimal = months.reduce(0) { runningTotal, dept in
                let payRate = dept.payRate(forYearsWorked: index + 1)
                let monthlyToRoth = Calculations.computeMonthlyRoth(
                    hours: monthlyHoursWorked,
                    atPayRate: payRate,
                    percentToRoth: percentToRoth
                )
                return runningTotal + monthlyToRoth
            }
            toRoth.append(yearToRoth)
            //chunk the toRoth array into 12 month groups and then reduce each to an average
            let yearsAverageOfMonthlyRothDepositsRaw = toRoth.chunked(into: 12)
            let yearsAverageOfMonthlyRothDeposits = yearsAverageOfMonthlyRothDepositsRaw.reduce(0, +) / yearsAverageOfMonthlyRothDepositsRaw.count   //<- Error #1
            for idx in yearsAverageOfMonthlyRothDeposits.indices {
                let principal = idx == 0
                                ? existing401kbalance
                                : yearsAverageOfMonthlyRothDeposits[idx - 1]
                let balance401k = calc401k(principal: principal,
                                           deposits: yearsAverageOfMonthlyRothDeposits[idx],
                                           rateOfReturn: 10
                )
                return balance401k  //<- Error #2
            }
            rothBalances.append(balance401k)
        }
        return toRoth; balance401k //<- Error #3
    }

The rest of the code...

extension Employee {
    func cumulativeSavingsSinceFirstHired() -> [Decimal] {
        //get our savings amount for each year of employment
        let savingsByYear = yearlySavings()
        //create an array to hold the cumulative savings for each year
        //we initialize each slot as 0
        //this saves us having to do a bunch of appends
        var cumulativeSavings: [Decimal] = .init(repeating: .zero, count: savingsByYear.count)
        //now loop through the yearly savings
        for idx in savingsByYear.indices {
            //and use reduce on a slice of the array from the startIndex (i.e., 0)
            //  to the current idx
            //so 0...0, then 0...1, then 0...2, etc
            cumulativeSavings[idx] = savingsByYear[0...idx].reduce(0) { accum, yrAmount in
                //simply add the cumulative total and the amount for the year
                accum + yrAmount
            }
        }
        //send it back out
        return cumulativeSavings
    }
}

extension Employee {
    func yearlyDepartments() -> [String] {
        let years = employmentHistory.chunked(into: 12)
        let departments: [String] = years.map { months in
            var seenDepts: Set<String> = []
            return months.reduce(into: [String]()) { acc, dept in
                if seenDepts.insert(dept.rawValue).inserted {
                    acc.append(dept.rawValue)
                }
            }.joined(separator: ",") //concat the final result with , separator
        }
        return departments
    }
    func yearlyDepartmentsEarningsAndSavings() -> [(String, (Decimal, Decimal))] {
        let yearlyDepartments = yearlyDepartments()
        let yearlyEarnings = yearlyEarnings()
        let yearlySavings = yearlySavings()
        let yearlyDepartmentsEarningsAndSavings: [(departments: String, (earnings: Decimal, savings: Decimal))] =
            Array(zip(yearlyDepartments, zip(yearlyEarnings, yearlySavings)))
        return yearlyDepartmentsEarningsAndSavings
    }
}

var birthdate = DateComponents(calendar: .current, year: 1965, month: 7, day: 20).date!
var hiredate = DateComponents(calendar: .current, year: 2021, month: 12, day: 28).date!
var promotion1date = DateComponents(calendar: .current, year: 2023, month: 12, day: 28).date!
var firstDeptMonths: Int { Calendar.current.dateComponents([.month], from: hiredate, to: promotion1date).month! }
var promotion2date = DateComponents(calendar: .current, year: 2025, month: 6, day: 28).date!
var secondDeptMonths: Int { Calendar.current.dateComponents([.month], from: promotion1date, to: promotion2date).month! }
var promotion3date = DateComponents(calendar: .current, year: 2026, month: 12, day: 28).date!
var thirdDeptMonths: Int { Calendar.current.dateComponents([.month], from: promotion2date, to: promotion3date).month! }
var retireAge = 65
let retireDate = Calendar.current.date(byAdding: .year, value: retireAge, to: birthdate)!
var fourthDeptMonths: Int { Calendar.current.dateComponents([.month], from: promotion3date, to: retireDate).month! }

extension Date {
    var age: Int { Calendar.current.dateComponents([.year], from: self, to: Date()).year! }
    var yearGroup: Int { Calendar.current.dateComponents([.year], from: self, to: Date()).year! + 1}
    var totalYearsRemaining: Int { Calendar.current.dateComponents([.year], from: self, to: retireDate).year! }
    var totalMonthsRemaining: Int { Calendar.current.dateComponents([.month], from: self, to: retireDate).month! }
}
//age
let age = birthdate.age
//number of years employee has worked for the company
let yearGroup = hiredate.yearGroup

func printYearlyDepartmentsAndEarnings(_ yearlyDeptsAndEarnings: [(departments: String, (earnings: Decimal, savings: Decimal))]) {
    for (year, deptsAndEarnings) in yearlyDeptsAndEarnings.enumerated() {
        print("Department(s) for Year \(year + yearGroup + 1): \(deptsAndEarnings.departments)")
        print("Earnings for Year \(year + yearGroup + 1): \(deptsAndEarnings.1.earnings)")
        print("Savings for Year \(year + yearGroup + 1): \(deptsAndEarnings.1.savings)")
        print("Age: \(year + age)")
        print("")
    }
}

func printSavingsBalance(_ savingsBalance: [(Decimal)]) {
    for (year, save) in savingsBalance.enumerated() {
        print("Savings Balance Year \(year + yearGroup + 1): \(save)")
    }
}
//create a new employee
var charlotte = Employee("Charlotte Grote", birthdate: birthdate, hiredate: hiredate, retirementAge: 65, department: .other("Hospitality"), monthlyHoursWorked: 160, percentToSave: 10.0, existing401kbalance: 1000, percentToRoth: 10.0, percentToTrad: 16.0)
//give our employee some work history
//months in Hospitality
charlotte.work(months: firstDeptMonths)
//ohh, a promotion!
charlotte.promote(to: .sales)
//months in Sales
charlotte.work(months: secondDeptMonths)
//another promotion. way to go, Charlotte!
charlotte.promote(to: .management)
//months in Management
charlotte.work(months: thirdDeptMonths)
//another promotion. way to go, Charlotte!
charlotte.promote(to: .seniorManagement)
//months in Senior Management
charlotte.work(months: fourthDeptMonths)
//how much money has Charlotte made after all this time?
printYearlyDepartmentsAndEarnings(charlotte.yearlyDepartmentsEarningsAndSavings())
printSavingsBalance(charlotte.cumulativeSavingsSinceFirstHired())

Error 1: Cannot convert value of type '(Int) -> Int' to expected argument type '(Int, [Decimal]) throws -> Int'

Error 2 & 3: Cannot find 'balance401k' in scope

Error 1 leads me to believe that using .reduce in this way is limited to Int parameters. Is that correct?

I want to be able to later print the yearly sum totals of each year's roth deposits. I think that will be possible by referencing the yearsAverageOfMonthlyRothDeposits array, but I need to clear all these errors first to find out.

I also want to be able to later print each year's 401k balances.

2      

Taking your depositsToRoth function and adding some comments to help me figure out what is intended and what is actually going on...

func depositsToRoth() -> [Decimal] {
    //chunk the employmentHistory into years (i.e., 12-month groups
    let years = employmentHistory.chunked(into: 12)

    //how much does the employee put into their Roth account each year?
    var toRoth: [Decimal] = []

    //balance in the employee's Roth account on a yearly basis
    var rothBalances: [Decimal] = []

    for (index, months) in years.enumerated() {
        //for one year, how much was contributed by the employee
        //  to their Roth account?
        let yearToRoth: Decimal = months.reduce(0) { runningTotal, dept in
            //what is the payRate for this month given a department
            //  and a number of years worked?
            let payRate = dept.payRate(forYearsWorked: index + 1)

            //calculate how much was put into the Roth account
            // this month
            let monthlyToRoth = Calculations.computeMonthlyRoth(
                hours: monthlyHoursWorked,
                atPayRate: payRate,
                percentToRoth: percentToRoth
            )

            //add the monthly Roth amount to the amounts for the
            //  rest of the amounts in this year
            return runningTotal + monthlyToRoth
        }

        //add this year's amount to the array of other year amounts
        toRoth.append(yearToRoth)

        //----- Here's where we run into issues -----

        //chunk the toRoth array into 12 month groups and then reduce each to an average
        let yearsAverageOfMonthlyRothDepositsRaw = toRoth.chunked(into: 12)
        let yearsAverageOfMonthlyRothDeposits = yearsAverageOfMonthlyRothDepositsRaw.reduce(0, +) / yearsAverageOfMonthlyRothDepositsRaw.count   //<- Error #1
        for idx in yearsAverageOfMonthlyRothDeposits.indices {
            let principal = idx == 0
                            ? existing401kbalance
                            : yearsAverageOfMonthlyRothDeposits[idx - 1]
            let balance401k = calc401k(principal: principal,
                                       deposits: yearsAverageOfMonthlyRothDeposits[idx],
                                       rateOfReturn: 10
            )
            return balance401k  //<- Error #2
        }
        rothBalances.append(balance401k)
    }

    return toRoth; balance401k //<- Error #3
}

Breaking things down...

let yearsAverageOfMonthlyRothDepositsRaw = toRoth.chunked(into: 12)

There is no need to chunk toRoth because it is already an array of yearly amounts. We chunk employmentHistory because it is an array of month data that we want to group by 12 to get a year's worth of data.

let yearsAverageOfMonthlyRothDeposits = yearsAverageOfMonthlyRothDepositsRaw.reduce(0, +) / yearsAverageOfMonthlyRothDepositsRaw.count

You get the error here because since you've chunked toRoth, then yearsAverageOfMonthlyRothDepositsRaw has a type of [[Decimal]] instead of [Decimal]. So when you try to reduce it, your function looks like this:

let yearsAverageOfMonthlyRothDeposits = yearsAverageOfMonthlyRothDepositsRaw.reduce(0) { accum, current in
    //accum is an Int because you kicked off the reduce with a 0
    //current is [Decimal] because yearsAverageOfMonthlyRothDepositsRaw is [[Decimal]]
}

I think your for loop looks mostly okay from a code standpoint, but we'll need to fix it up once we address all the other issues. Put a pin in it.

Except for this line:

return balance401k

If you return from inside a for loop, your loop ends right there and then. So if you need to keep processing items, you can't.

rothBalances.append(balance401k)

This is where you actually get the first error about balance401k not being found. And that's because this line is outside the loop but balance401k is declared inside the loop. Thus the "out of scope" message.

return toRoth; balance401k

You get an error here because balance401k is defined inside the loop but this statement is outside the loop, and also ; creates a second statement on the line that causes the compiler to throw a warning. I'm not entirely sure what balance401k is even doing here, to be honest.

Also, you are returning toRoth, but you just spent a loop setting up rothBalances and then do nothing with it.

Let me know if any of this makes sense.

2      

And here is depositsToRoth rewritten...

func depositsToRoth() -> [Decimal] {
    //chunk the employmentHistory into years (i.e., 12-month groups
    let years = employmentHistory.chunked(into: 12)

    //start with an empty array of how much the employee
    //  puts into their Roth account each year
    var averageRothContribution: [Decimal] = []

    for (index, months) in years.enumerated() {
        //for one year, how much was contributed by the employee
        //  to their Roth account?
        let yearToRoth: Decimal = months.reduce(0) { runningTotal, dept in
            //what is the payRate for this month given a department
            //  and a number of years worked?
            let payRate = dept.payRate(forYearsWorked: index + 1)

            //calculate how much was put into the Roth account
            //  this month
            let monthlyToRoth = Calculations.computeMonthlyRoth(
                hours: monthlyHoursWorked,
                atPayRate: payRate,
                percentToRoth: percentToRoth
            )

            //add the monthly Roth amount to the amounts for the
            //  rest of the amounts in this year
            return runningTotal + monthlyToRoth
        }

        //add the average of this year's amounts to the array of other year average amounts
        let averageAmount = yearToRoth / Decimal(months.count)
        averageRothContribution.append(averageAmount)
    }

    //averageRothContribution now contains the average amount
    //  in each year that the employee contributed

    //start with an empty array of the balance in the
    //  employee's Roth account on a yearly basis
    var rothBalances: [Decimal] = []

    for idx in averageRothContribution.indices {
        //our principal will either be existingBalance (for the first item)
        //  or the previous item (for items 2...n)
        let principal = idx == 0
        ? existing401kbalance
        : averageRothContribution[idx - 1]

        //calculate the yearly balance
        let balance401k = calc401k(principal: principal,
                                   deposits: averageRothContribution[idx],
                                   rateOfReturn: 10
        )

        //and add it to our list of balances
        rothBalances.append(balance401k)
    }

    //give it back to the caller
    return rothBalances
}

Using this rewritten function, when I run the project, I get this output:

Department(s) for Year 3: Hospitality
Earnings for Year 3: 19200
Savings for Year 3: 1920
Age: 57

Department(s) for Year 4: Hospitality
Earnings for Year 4: 23040
Savings for Year 4: 2304
Age: 58

Department(s) for Year 5: Sales
Earnings for Year 5: 46080
Savings for Year 5: 4608
Age: 59

Department(s) for Year 6: Sales,Management
Earnings for Year 6: 59520
Savings for Year 6: 5952
Age: 60

Department(s) for Year 7: Management
Earnings for Year 7: 72960
Savings for Year 7: 7296
Age: 61

Department(s) for Year 8: Senior Management
Earnings for Year 8: 96000
Savings for Year 8: 9600
Age: 62

Department(s) for Year 9: Senior Management
Earnings for Year 9: 99840
Savings for Year 9: 9984
Age: 63

Department(s) for Year 10: Senior Management
Earnings for Year 10: 103680
Savings for Year 10: 10368
Age: 64

Department(s) for Year 11: Senior Management
Earnings for Year 11: 53760
Savings for Year 11: 5376
Age: 65

Savings Balance Year 3: 1920
Savings Balance Year 4: 4224
Savings Balance Year 5: 8832
Savings Balance Year 6: 14784
Savings Balance Year 7: 22080
Savings Balance Year 8: 31680
Savings Balance Year 9: 41664
Savings Balance Year 10: 52032
Savings Balance Year 11: 57408

Is this what you are looking for?

2      

@roosterboy

Thank you for explaining my flaws in the chunk actions and for-in loop return!

I've added the following print function:

func printRothBalance(_ rothBalance: [(Decimal)]) {
    for (year, roth) in rothBalance.enumerated() {
        print("Roth 401k Balance Year \(year + yearGroup + 1): \(roth)")
    }
}

printRothBalance(charlotte.depositsToRoth())

Which returns the following:

Roth 401k Balance Year 3: 3115.20396231420428012956723300993944886
Roth 401k Balance Year 4: 2589.34316463809600490128513890250471354
Roth 401k Balance Year 5: 5037.2830566437059628789769804869266424
Roth 401k Balance Year 6: 6656.7315920034699602416631448627432051
Roth 401k Balance Year 7: 8187.8030819679301782771034984147580275
Roth 401k Balance Year 8: 10724.1200193688439155820411508229816967
Roth 401k Balance Year 9: 11338.3231072921543936752300163425894125
Roth 401k Balance Year 10: 11775.7721044248573131139272602145936474
Roth 401k Balance Year 11: 12213.2211015575602325526245040865978824

I'm confused on how the balance decreased in year 4. And generally why the balances are growing so slowly. Any ideas?

I ran the following in playground to see what results we should expect:

var yearOneRothBalance = calc401k(principal: 1000, deposits: 1920/12,rateOfReturn: 10.0)
var yearTwoRothBalance = calc401k(principal: yearOneRothBalance, deposits: 4224/12, rateOfReturn: 10.0)
var yearThreeRothBalance = calc401k(principal: yearTwoRothBalance, deposits: 8832/12, rateOfReturn: 10.0)

yearOneRothBalance  // 3115.20396231420428012956723300993944886
yearTwoRothBalance // 7864.4864936338034283135300277731831885
yearThreeRothBalance // 17936.2591146482235336136415751132136957

2      

Ack, I think I messed up the logic in the for loop. See if this is more like what you were expecting:

func depositsToRoth() -> [Decimal] {
    //chunk the employmentHistory into years (i.e., 12-month groups
    let years = employmentHistory.chunked(into: 12)

    //start with an empty array of how much the employee
    //  puts into their Roth account each year
    var averageRothContribution: [Decimal] = []

    for (index, months) in years.enumerated() {
        //for one year, how much was contributed by the employee
        //  to their Roth account?
        let yearToRoth: Decimal = months.reduce(0) { runningTotal, dept in
            //what is the payRate for this month given a department
            //  and a number of years worked?
            let payRate = dept.payRate(forYearsWorked: index + 1)

            //calculate how much was put into the Roth account
            //  this month
            let monthlyToRoth = Calculations.computeMonthlyRoth(
                hours: monthlyHoursWorked,
                atPayRate: payRate,
                percentToRoth: percentToRoth
            )

            //add the monthly Roth amount to the amounts for the
            //  rest of the amounts in this year
            return runningTotal + monthlyToRoth
        }

        //add this year's amount to the array of other year amounts
        averageRothContribution.append(yearToRoth / Decimal(months.count))
    }

    //averageRothContribution now contains the average amount
    //  in each year that the employee contributed

    //start with an empty array of the balance in the
    //  employee's Roth account on a yearly basis
    var rothBalances: [Decimal] = []

    for idx in averageRothContribution.indices {
        //our principal will either be existingBalance (for the first item)
        //  or the previously added item in rothBalances (for items 2...n)
        let principal = idx == 0
        ? existing401kbalance
        : rothBalances[idx - 1]

        //calculate the yearly balance
        let balance401k = calc401k(principal: principal,
                                   deposits: averageRothContribution[idx],
                                   rateOfReturn: 10
        )

        //and add it to our list of balances
        rothBalances.append(balance401k)
    }

    //give it back to the caller
    return rothBalances
}

And the results from printRothBalance():

Roth 401k Balance Year 3: 3115.20396231420428012956723300993944886
Roth 401k Balance Year 4: 5853.9955987608963897745354300607654945
Roth 401k Balance Year 5: 11292.1635823899802529271777112270741682
Roth 401k Balance Year 6: 18707.1224432569547352497187466561965481
Roth 401k Balance Year 7: 28305.8680178073722134124570576884100876
Roth 401k Balance Year 8: 41322.31675890502944286963025766616666
Roth 401k Balance Year 9: 56103.85595385001563979726361027458934
Roth 401k Balance Year 10: 72835.31363837603611857781045942813189
Roth 401k Balance Year 11: 91720.871758787622174674797995751069435

Although...

I notice from your Playground example code:

var yearOneRothBalance = calc401k(principal: 1000, deposits: 1920/12,rateOfReturn: 10.0)
var yearTwoRothBalance = calc401k(principal: yearOneRothBalance, deposits: 4224/12, rateOfReturn: 10.0)
var yearThreeRothBalance = calc401k(principal: yearTwoRothBalance, deposits: 8832/12, rateOfReturn: 10.0)

that you are passing into the deposits parameter the cumulative savings balance for each year and dividing that by 12. The code I posted is using the annual balance for each year and dividing by 12 to get the average monthly contribution. That accounts for some of the discrepancy. To be honest, I'm not sure which is the correct one to use; all this financial math stuff makes my eyes glaze over. I probably misunderstood what the requirements were.

3      

Save 50% in my WWDC sale.

SAVE 50% To celebrate WWDC24, all our books and bundles are half price, so you can take your Swift knowledge further without spending big! Get the Swift Power Pack to build your iOS career faster, get the Swift Platform Pack to builds apps for macOS, watchOS, and beyond, or get the Swift Plus Pack to learn advanced design patterns, testing skills, and more.

Save 50% on all our books and bundles!

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.