Value of type Int has no member reduce

Hi Guys,

I am just trying to get a value on my view to be a sum of stored values.

I have used the following code but get the error message “Value of type ‘Int’ has no member ‘reduce’”.

Any help here would be awesome.

I have also put the code in which references to IncomeDetails.

import SwiftUI

struct MainIncomeView: View {
    @Binding var income: [IncomeDetails]

    var body: some View {
        VStack {
            Text("Income")
                .font(.title)
                .padding()
            ForEach($income) { $income in
                Text("\(income.incomeAnnual.reduce(0, +))")
                    .font(.headline)
                    .padding(.bottom)
            }
        }
        .padding()
    }
}

struct MainIncomeView_Previews: PreviewProvider {
    static var previews: some View {
        NavigationView {
            MainIncomeView(income: .constant(IncomeDetails.sampledata))
            .previewLayout(.fixed(width: 200, height: 100))
    }
}
}

import Foundation

struct IncomeDetails: Identifiable {
    let id: UUID
    var incomeDetails: String
    var incomeFrequency: Period
    var incomeAmount: Int
    var incomeAnnual: Int
    
    init(id: UUID = UUID(), incomeDetails: String, incomeFrequency: Period, incomeAmount: Int, incomeAnnual: Int) {
        self.id = id
        self.incomeDetails = incomeDetails
        self.incomeFrequency = incomeFrequency
        self.incomeAmount = incomeAmount
        self.incomeAnnual = incomeAnnual
    }
}

extension IncomeDetails {

struct Data {
    var incomeDetails: String = ""
    var incomeFrequency: Period = .Weekly
    var incomeAmount: Int = 0
    var incomeAnnual: Int = 0
    }

var data: Data {
    Data(incomeDetails: incomeDetails, incomeFrequency: incomeFrequency, incomeAmount: incomeAmount, incomeAnnual: incomeAnnual)
}
    
    mutating func update(from data: Data) {
        incomeDetails = data.incomeDetails
        incomeFrequency = data.incomeFrequency
        incomeAmount = Int(data.incomeAmount)
        incomeAnnual = Int(data.incomeAnnual)
    }
    
    init(data: Data) {
        id = UUID()
        incomeDetails = data.incomeDetails
        incomeFrequency = data.incomeFrequency
        incomeAmount = Int(data.incomeAmount)
        incomeAnnual = Int(data.incomeAnnual)
    }
}

extension IncomeDetails {
    static let sampledata: [IncomeDetails] =
    [
        IncomeDetails(incomeDetails: "Wages", incomeFrequency: .Weekly, incomeAmount: 1000, incomeAnnual: 52000),
        IncomeDetails(incomeDetails: "Dividends", incomeFrequency: .Quarterly, incomeAmount: 500, incomeAnnual: 2000)
    ]
}

Perfect formatted question :clap::clap:

Reduce is a function of an array, not an integer, that’s what the issue is.

So you want a sum of the incomeAnnual property for every element in the income array?

Hi Mikaela,

ah ok makes sense. And yes thats exactly what I want to do.

Do you know what I need to do to achieve this?

Thanks

First easiest way I can think of is:

  • Create a lazy variable in your MainIncomeView and have it be a normal computer property
  • iterate through the income array (now you could use the reduce function)
  • and return that value as the value for the computed property
  • in the view use your computed property

Thanks Mikaela,

This is the code which I wrote (not sure if its correct) but I am still getting the same error. Can you please help work out where I am going wrong?

    private lazy var incomeAnnualSum = ForEach($income) { $income in
        income.incomeAnnual.reduce(0, +)
    }

You can’t use ForEach outside of a View's body property (or another property that returns some View or similar). You want incomeAnnualSum to return an Int, so ForEach is incorrect here. Your use of reduce is a good idea, but you’ve got the form wrong. You can’t just pass + as the function to use in the reduce, since you have to drill down into a property to get the value you want and can’t do a straight addition.

It should be:

private var incomeAnnualSum: Int {
    income.reduce(into: 0) { r, c in
        r += c.incomeAnnual
    }
}

But your logic doesn’t make any sense. By using:

ForEach($income) { $income in
...
}

You are calculating incomeAnnualSum for each member of your income array. But it will always be the same amount because incomeAnnualSum is calculated using every member of the income array!

Take out the ForEach and just have the Text there and that will be correct.

Thanks for this, I have updated the code accordingly (see below) but now I am getting an error on the Text(incomeAnnualSum) line saying “No exact matches in call to initializer”.

Any thoughts here?

import SwiftUI

struct MainIncomeView: View {
    @Binding var income: [IncomeDetails]
    
    private var incomeAnnualSum: Int {
        income.reduce(into: 0) { r, c in
            r += c.incomeAnnual
        }
    }

    var body: some View {
        VStack {
            Text("Income")
                .font(.title)
                .padding()
                Text(incomeAnnualSum)
                    .font(.headline)
                    .padding(.bottom)
        }
        .padding()
    }
}

struct MainIncomeView_Previews: PreviewProvider {
    static var previews: some View {
        NavigationView {
            MainIncomeView(income: .constant(IncomeDetails.sampledata))
            .previewLayout(.fixed(width: 200, height: 100))
    }
}
}

Actually looks like I got it working sorry, just had to wrap it in a Int.

Thank you again for your help!

import SwiftUI

struct MainIncomeView: View {
    @Binding var income: [IncomeDetails]
    
    private var incomeAnnualSum: Int {
        income.reduce(into: 0) { r, c in
            r += c.incomeAnnual
        }
    }

    var body: some View {
        VStack {
            Text("Income")
                .font(.title)
                .padding()
                Text("\(Int(incomeAnnualSum))")
                    .font(.headline)
                    .padding(.bottom)
        }
        .padding()
    }
}

struct MainIncomeView_Previews: PreviewProvider {
    static var previews: some View {
        NavigationView {
            MainIncomeView(income: .constant(IncomeDetails.sampledata))
            .previewLayout(.fixed(width: 200, height: 100))
    }
}
}

You don’t need to wrap it in an Int if you add a format parameter, as I mentioned in the other thread:

Text(computedIncomeAmount, format: .number)