1

My question is, how would one derive a total sum for a single property irrespective of any relationship for every saved instance of a Core Data entity?

This CoreData question is different from the others that I have seen because the others speak on deriving a property from a relationship defined in the CoreData GUI. There is no relationship here. I would just like to get the total for every saved instance on a single CoreData entity*.

Hopefully the video will help.

Big Picture

Goal: Mapping over all a single CoreData Property for every instance of the entity.

Core Data example video

I have been doing a lot of research on this lately and am currently waiting on a response from the Apple technical team. First let me say that I have watch the WWDC Vid on Core Data and it is very helpful.

But they speak about how to derive properties from multiple relationships. This is a single relationship that I would like to map over.

And all other resources that I've checked on the topic, (a good deal can be found here). Thanks to @lorem ipsum for the resources he shared.

But these resources again speak about how to aggregate properties from a different relationship.

So just to be clear, how would one derive a total sum for a single property irrespective of any relationship for every saved instance of a Core Data entity.

I would be happy if someone can even suggest how I would get an array or dictionary of all the saved instances.

Adding more detail

This is the class I am using (simplified)

   import Foundation
   import CoreData

@objc(Ticket)
public class Ticket: NSManagedObject { }


extension Ticket {
    @NSManaged var company: String
    @NSManaged var hoursWorked: Int64
 }

And here is a mock func to try and convey the idea of what I am trying to do:

This is a completely bogus func just hoping to add clarity to what I'm trying to do.

  func getAllHoursForCompany(_ company: String, using context: NSManagedObjectContext) -> Int {
  var allHours = [Int]()
  let workingCompany: Ticket(context: context) where Ticket.company == company

   for hours in workingCompany {
  allHours.append(hours)

}
  return allHours.reduce(0) { $0 + $1 }
}
halfer
  • 19,824
  • 17
  • 99
  • 186
Sergio Bost
  • 2,591
  • 2
  • 11
  • 29
  • Does this answer your question? [Core Data sum of all instances attribute](https://stackoverflow.com/questions/14822618/core-data-sum-of-all-instances-attribute). And you would of course need to use a predicate as well to only calculate a sum for a company – Joakim Danielson Mar 22 '21 at 16:13
  • @JoakimDanielson I added the class and a mock function, hopefully that clarifies my idea. And yes that answer that I accepted works for counting a property from another relationship. But check out this image: https://atenivel.sirv.com/Screen%20Shot%202021-03-22%20at%2012.11.31%20PM.png That answer works for this, see how its a property calculating a property from a whole other relationship, well this CoreData model does not have any relationships.. hope that clarified.. – Sergio Bost Mar 22 '21 at 16:16
  • @JoakimDanielson That looks like its it... wow this never came up in my searches.. let me give this a try.. – Sergio Bost Mar 22 '21 at 16:17
  • @JoakimDanielson that works perfectly, except this will give me all hours for every entry, I'm trying to figure out how I would get all hours for "Verizon" or "T-Mobile" etc. – Sergio Bost Mar 22 '21 at 16:46
  • 1
    Yes I know and that is why I said you need a predicate – Joakim Danielson Mar 22 '21 at 16:50
  • @JoakimDanielsonm Getting the error `sum is not a supported method`, which an unanswered question on SO here: https://stackoverflow.com/questions/33977507/running-sum-from-coredata-swift will research further.. – Sergio Bost Mar 22 '21 at 17:06
  • Another option is of course to fetch all objects for a company and use a high order function to calculate the sum, `results.reduce(0) { $0 + $1.hoursWorked }`. Maybe the easiest actually and it should work fine if you don't have a massive amount of objects. – Joakim Danielson Mar 22 '21 at 17:58
  • @JoakimDanielson which isn't a bad idea. Right now I'm trying to see to at what point will I have an array of my CoreData type to even begin that process. – Sergio Bost Mar 22 '21 at 18:41

1 Answers1

0

So I found out the answer to the question, hopefully it will help someone else:

 

let sumExpression = NSExpression(format: "sum:(hoursWorked)")
        let sumExpressionDesc = NSExpressionDescription()
        sumExpressionDesc.name = "sum"
        sumExpressionDesc.expression = sumExpression
        sumExpressionDesc.expressionResultType = .integer64AttributeType

        let fetchRequest: NSFetchRequest<Book> = Book.fetchRequest()
        fetchRequest.resultType = .dictionaryResultType
        fetchRequest.propertiesToFetch = ["company", sumExpressionDesc]
        fetchRequest.propertiesToGroupBy = ["company"]

        let result = try? persistentContainer.viewContext.execute(fetchRequest)

        if let fetchResult = result as? NSAsynchronousFetchResult<NSDictionary>,
           let nsDictionary = fetchResult.finalResult  {
            print(nsDictionary)
        }

That is for a CodeGen of manual and this goes into the extension as a func

Sergio Bost
  • 2,591
  • 2
  • 11
  • 29