Nesting statements rather than creating new single-use objects? - objective-c

Newb question here.
Why would I do this:
NSPredicate *pred = [NSPredicate predicateWithFormat:#"(lineNum = %d)", i];
[request setPredicate:pred];
When I could just do this?
[request setPredicate:[NSPredicate predicateWithFormat:#"(lineNum = %d)", i]];
Every textbook code example I find uses the first method, but as far as I can see the second method will basically do the same thing and just looks neater. "pred" is only called once so why create it as an object?

This has to do primarily with the format of text books: it is hard to fit more than a certain number of characters on a page, because books have no scroll bars. Other than that, the two are identical.
One reason to do it in real life is so that you could set a break point and examine pred before calling setPredicate:.

Which one looks neater is just a matter of preference.
There is no functional difference between the two.
For more complex cases, the first pattern allows for more self-explanatory code, because you can put meaningful variable names:
NSPredicate *correctLineNumber =
[NSPredicate predicateWithFormat:#"(lineNum = %d)", i];
[request setPredicate:correctLineNumber];

Related

NSPredicate to get a single attribute back from multiple objects in an array

This seems like it would be fairly simple to do, but I can't find anything referencing this specifically.
Say I have an array of Post objects that each have an objectId. How can I create an array of only the objectId's?
something like:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"objectId ALL"]; // I just want all the objectIds...
NSArray *results = [[self.objects filteredArrayUsingPredicate:predicate] mutableCopy];
or
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"objectId matches {anything}"];
NSPredicate cannot return anything other than BOOL, so it cannot return objectId. Moreover, filteredArrayUsingPredicate: can change the number of items in your array, but it will keep the objects themselves unchanged.
If you want to start with one object and produce another object, you need NSExpression. It can transform your object into something else - for example, by projecting one of its attributes. However, Cocoa offers no built-in way of applying NSExpression to an NSArray in a way similar to filteredArrayUsingPredicate:, so NSExpression would not help here.
You can solve this by using a little trick, though:
NSString *key = NSStringFromSelector(#selector(objectId));
NSMutableArray *allIds= [[array valueForKey:key] mutableCopy];
If you use this in production code, it is a good idea to document it heavily by a few lines of comments. Otherwise, the readers of your code (including you in about six months or so) are virtually guaranteed to get confused.
Ok so it turns out this is not a task for NSPredicate, which is why I couldn't find out how to do it using that. Instead I can do this pretty easily with key-value coding (KVC).
If you want to simply retrieve something and not filter then:
NSArray *results = [self.objects valueForKey:#"objectId"];

NSPredicate filter for many-to-many-to-many relationships

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?
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];

CoreData's NSFetchrequest: Provide classname and propertynames moredynamically

In CoreData, when you want to query for an object, you have to specify the name of the entity and the names of the properties in strings like that:
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
fetchRequest.entity = [NSEntityDescription entityForName:#"Person" inManagedObjectContext:context];
fetchRequest.predicate = [NSPredicate predicateWithFormat:#"Name=%# AND Forename=%#", name, vorname];
I don't really like this approach, because in doing so, the IDE can't help me in scenarios like renaming one of the properties or renaming the classname.
Using this code-block, I made my call a little more dynamic by getting the classname by introspection:
NSString *classname = [[Person class] description];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
fetchRequest.entity = [NSEntityDescription entityForName:classname inManagedObjectContext:context];
fetchRequest.predicate = [NSPredicate predicateWithFormat:#"Name=%# AND Forename=%#", name, vorname];
Is there any way to get the name of the properties (Name and Forename) the same way?
I don't know how to handle properties dynamically.
Do you get my point? I'd be happy, if someone could point me into the right direction.
I've used plists in the resource bundle in this way, sort of-- basically, create an NSDictionary that maps keys that are your parameters to the names of the properties on the object at hand, then just name it MyClassname.plist for easy loading into memory. You'd have to write one up for every class you want this for, though. (and edit them if you change the property names)
Not sure if there is an easier way.
And, IIRC, you sub in the keys as so:
[NSPredicate predicateWithFormat:#"%k=%#",[plist objectForKey:#"param1"],[personObject valueForKey:[plist objectForKey:#"param1"]]]
I'm not sure I see a big advantage of entityForName:[[Person class] description] over entityForName:#"Person" (or maybe entityForName:PersonEntityName where PersonEntityName is just a constant). It works fine provided that the name of the class that represents an entity is the same as the name of the entity, but that's often not the case. It's quite common to use plain old NSManagedObject for simple entities, and that obviously doesn't match the entity name.
Likewise, if you're creating a fetch request, the names of the properties that you're interested in are usually already determined and unlikely to change, so specifying them by name is usually the easiest thing to do. You can get an array of properties from the entity description, but how will you know which property to use if you don't already know its name? And if you know the name of the property, there's not much sense in retrieving the property description just so you can get its name. ;-)
There are times when it's helpful to leave the entity and properties undetermined until the application runs, such as when you want to let the user specify the parameters of the search. That's clearly not the case in the example you gave, though, because you've given the format of the predicate.

need help on a simple predicate to match any word in a string to a property

Let's say i want to let user search for my objects using a name property of the objects.
I have no problem if the user only enters one word:
e.g: facial
My predicate will be:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"name CONTAINS[cd] %#", word];
But what if user enter more than one word separated by space?
I want to do sth like:
NSArray *words = [query componentsSeparatedByString:#" "];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"name CONTAINS[cd] ANY %#", words];
But it doesnt work. Any guidance?
Thanks!
Another way of doing this (and I just learnt this myself as a result of your question) is to use subqueries. Check this SO question for more details. You can use subqueries in the following manner –
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SUBQUERY(%#, $str, SELF CONTAINS[cd] $str).#count != 0", words];
NSLog(#"%#", [array filteredArrayUsingPredicate:predicate]);
This seems to work as I've tested it myself but this could also be the arcane & obscure way that Dave has mentioned as it finds no mention in the Predicate Programming Guide.
The format for a SUBQUERY can be found here. It's the same link that you will find in the question linked earlier.
As you mentioned (correctly) in the comment, you can do this by building a compound predicate predicate, although you'll want to use orPredicateWithSubpredicates, and not the and variant.
That really is the best way to do this. There are others, but they rely on more arcane and obscure uses of NSPredicate, and I really recommend going with the build-a-compound-predicate route.

iPhone's Core Data crashes on fetch request

I'm using the following code to grab a few objects from SQLite store (which is a prepared SQLite db file, generated with Core Data on desktop):
NSFetchRequest * request = [[NSFetchRequest alloc] init];
[request setEntity: wordEntityDescription];
[request setPredicate: [NSPredicate predicateWithFormat: #"word = %#", searchText]];
NSError * error = [[NSError alloc] init];
NSArray * results = [[dao managedObjectContext] executeFetchRequest: request error: &error];
Eveyrthing seems to be setup properly, but executeFetchRequest:error: fails deeply inside Core Data (on NSSQLCore _newRowsForFetchPlan:selectedBy:withArgument) producing 256 error to the outside code.
The only kink I had setting up managedObjectContext is I had to specify NSIgnorePersistentStoreVersioningOption option to addPersistentStoreWithType as it was constantly producing 134100 error (and yes, I'm sure my models are just identical: I re-used the model from the project that produced the SQL file).
Any ideas?
P.S. Don't mind code style, it's just a scratch pad. And, of course, feel free to request any additional info. It would be really great if someone could help.
Update 1
Alex Reynolds, thanks for willingness to help :)
The code (hope that's what you wanted to see):
NSEntityDescription * wordEntityDescription; //that's the declaration (Captain Obviousity :)
wordEntityDescription = [NSEntityDescription entityForName: #"Word" inManagedObjectContext: ctx];
As for predicate – never mind. I was removing the predicate at all (to just grab all records) and this didn't make any differences.
Again, the same code works just fine in the desktop application, and that drives me crazy (of course, I would need to add some memory management stuff, but it at least should produce nearly the same behavior, shouldn't it?)
Can you add code to show how wordEntityDescription is defined?
Also, I think you want:
NSError *error = nil;
You may want to switch the equals symbol to like and use tick marks around the searchText field:
[request setPredicate: [NSPredicate predicateWithFormat: #"word like '%#'", searchText]];
NSPredicate objects are not put together like SQL, unfortunately. Check out Apple's NSPredicate programming guide for more info.