3
votes

I'm still fairly new to using NSPredicate, but I've got a somewhat complicated issue and I'm not quite sure the best way to go about a solution.

I'm attempting to build a filter for an entity called Instrument, which can be used with many Procedures. Procedures, in turn, are related to one or more Diseases. Diseases are related to many Procedures, and Instruments can be used in more than one Procedure. In other words, I have the following many-to-many-to-many relationship:

Instruments <-> Procedures <-> Diseases

What I would like to do is filter Instruments using Disease.title. In a simple and magical world, this would be as simple as

//where predicate is used on the Instruments array
[NSPredicate predicateWithFormat:@"(procedures.diseases.title == %@)", diseaseTitleString];

Stepping through this piece by piece (with a few typos and misnamings), I found that the filter knows the fields of Procedures and Diseases enough to tell me that "diseaseTypes" is not a field on procedures, and "name" is not a field on diseases. Naivety led me to hoping that this simple predicate would work.

I get nothing.

I've also tried using (ALL procedures.diseases.title == %@), but that doesn't work either.

I've seen some things about sub-predicates, but nothing that really sticks out to me as something that works or makes a lot of sense to me.

Basically I want to get the list:

NSArray *instruments = [[NSArray alloc] init];
for (Disease *disease in Diseases) {
  if ([disease.title isEqualToString:titleString]) {
    for (Procedure *procedure in disease.procedures) {
      for (Instrument *instrument in procedure.instruments) {
        if (![instruments containsObject:instrument]) {
          [instruments addObject:instrument];
        }
      }
    }
  }
}

Right now, I'm using this method to get an array that I can filter based on other predicates, and it's working fine, but it's ugly and I feel like there is a better, cleaner way do filter this. Can anyone help educate me on the finer uses of NSPredicate design?

1

1 Answers

2
votes

On the predicate you have above, replace the double equals with a single equals. Also add an "ANY" clause. Finally, remove the parentheses. So you will have:

[NSPredicate predicateWithFormat:@"ANY procedures.diseases.title = %@", diseaseTitleString];

In general, I find a good way to handle complex predicates is to build them up using sub-predicates.

So, you start with:

        NSMutableArray *subpredicates = [NSMutableArray array];

Then bit by bit you add your subpredicates:

[subpredicates addObject:
    [NSPredicate predicateWithFormat:@"ANY procedures.diseases.title = %@",
         diseaseTitleString]]

[subpredicates addObject:anotherPredicate etc];

Finally, create your "and" or "or" predicate, for example:

 NSPredicate *andPredicate = [NSCompoundPredicate andPredicateWithSubpredicates:subpredicates];