0

The question is this: how do you group Core Data objects by attribute and then perform simple calculations (let's say, sum) on them?

I've found a few similar answers for this (like this one), but none seem conclusive and all are geared up towards Swift (whereas I am working on SwiftUI).

For example, I have a Core Data model that looks like this:

extension Items {

    @nonobjc public class func fetchRequest() -> NSFetchRequest<Items> {
        return NSFetchRequest<Items>(entityName: "Items")
    }

    @NSManaged public var id: UUID?
    @NSManaged public var cost: Int16
    @NSManaged public var type: String
}

In the Home() view, I'd like to group by type and then find the total amount paid (i.e. sum of cost) for all objects in each type.

So far, I've tried making a special group() function (a bit like in this question), but get the error message "Cannot find 'i' in scope".

struct Home: View {
    
    @Environment(\.managedObjectContext) private var viewContext
    
    @FetchRequest(entity: Items.entity(), sortDescriptors: [NSSortDescriptor(keyPath: \Items.type, ascending: true)]) var items: FetchedResults<Items>

var body: some View {
        ForEach(group(items), id: \.self) { [i] in
            Text("\([i].cost.reduce(0,+))")
           }
      }
}

func group(_ result : FetchedResults<Items>)-> [[Items]] {
        
    return Dictionary(grouping: result) { $0.type }
            .sorted(by: {$0.key < $1.key})
            .map {$0.value}
        
    }

Surely this can't be so hard? Any help very welcome!

4
  • 1
    You should not have brackets around the i in [i] in. Brackets like that capture a value, but you don't have a variable to capture -- you're just getting a parameter for the closure. Same think on the next line -- no brackets there either. There may be issues beyond this as well -- it would be good to see the source for Clothing Commented Apr 13, 2021 at 22:07
  • Thanks, I would love this to be the answer but when I try I get the message "Value of type '[Items]' has no member 'cost'". Clothing should read Items, too (apologies, this was a mistake and I corrected it in the question). Commented Apr 13, 2021 at 22:10
  • 1
    Yeah, like I said, there may be additional issues. You're returning a multidimensional array from group, so you have to unwrap that first. Maybe you mean i.map { $0.cost }.reduce(0,+) Commented Apr 13, 2021 at 22:12
  • Nice, this worked perfectly! Feel free to submit this as an answer if you'd like :-) Many thanks in either case. Commented Apr 13, 2021 at 22:15

1 Answer 1

1

You should not have brackets around the i in [i] in. Brackets like that capture a value, but you don't have a variable to capture -- you're just getting a parameter for the closure.

ForEach(group(items), id: \.self) { i in

On your next line, you're dealing with a multidimensional array. You can't access a property directly, but you could map first:

Text("\(i.map(\.cost).reduce(0,+))")

You could also structure this slightly differently and remove the map by doing:

i.reduce(0,{ $0 + $1.cost })
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.