How do I count relationships with a particular column value from an NSFetchRequest?

87 views Asked by At

I currently have 2 entities:

  • List
  • Item

This is a one-to-many relationship, in which a List can have many Items.

Items have a column called completed.

I want to obtain the count of a Lists completed Items from my NSFetchRequest.

I know that I can simply do List.Items.count to obtain a count of ALL of a Lists Items, but I've no idea how to get the count of completed items as well.

How do I count relationships with a particular column value from an NSFetchRequest, or even some type of filter/where of List.Items.count?

Edit

I had to rename List to MyList and Item to MyItem.

  • MyList -> MyItem is one-to-many in Core Data (so I can use myList.myItems in the fetched results)
  • MyItem -> MyList is many-to-one in Core Data (so I can use myItem.myList in the fetched results)

Here is how I fetch the data. First I use an extension:

extension MyList {
    static func getAllMyLists() -> NSFetchRequest<MyList> {
        let request: NSFetchRequest<MyList> = MyList.fetchRequest()
        let sortDescriptor = NSSortDescriptor(key: "sortOrder", ascending: true)

        request.sortDescriptors = [sortDescriptor]

        return request
    }
}

Then I use a @FetchRequest in the view struct:

@FetchRequest(fetchRequest: MyList.getAllMyLists()) var myLists: FetchedResults<MyList>

Like I said I have no clue how to add the predicate I want to this so that the results include a count of the completed MyItems...I'm using a terrible approach in my answer by not utilizing the fetch request to its full potential due to lack of knowledge.

2

There are 2 answers

2
vadian On

There are two ways:

  • If you have the reference to the List object just filter the NSSet relationship

    let numberOfCompletedItems = (list.items as! Set<MyItem>).filter{$0.completed}.count
    

    You can avoid the forced type cast if you declare the to-many relationship as native Set<MyItem>

  • Otherwise fetch the items and add the predicate to the fetch request

    • "TheList" is the name of the list. If there is no name attribute use something unique.
    • context is the reference to the managed object context
    • It's assumed that there is a inverse relationship list in MyItem

      let listName = "TheList" 
      let request: NSFetchRequest<MyItem> = MyItem.fetchRequest()
      request.predicate = NSPredicate(format: "list.name == %@ AND completed == true", listName)
      do {
         let completedItems = try context.fetch(request)
         let numberOfCompletedItems = completedItems.count
      } catch { print(error) }
      

And please name variables according to the naming convention starting with a lowercase letter

3
stardust4891 On

dahiya_boy and Joakim were both right in the comments, I had to use an NSPredicate with filtered:

let completedPredicate: NSPredicate = NSPredicate(format: "completed == true")
let completedCount = List.Items.filtered(using: completedPredicate).count