Puzzled with NSPredicate predicateWithFormat table - objective-c

both lines of code work but not as I would expect. The first one, using a variable skips only the first item of the table and updates all the remaining items. Using a string literal in the second option doesn't and works the way I want. Why is this happening??
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"myString == %#", date];
OR
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"myString == '5/24/14'"];

To see why this is happening, try logging date:
NSLog(#"%#", date);
You will see that the resulting string (in the console) does not look at all like '5/24/14'! But that (what you see in the console) is the string that you are using in your first code.
So it's all about how the NSDate is covered to an NSString. How to get around this? Use an NSDateFormatter to convert the date to a string in the form you're after!

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

Objective-C - wrap attribute value with commas in NSPredicate CONTAINS

Here is the situation. I have some Core Data data and I want to get the data where the value of an attribute wrapped with two commas contains another string.
This is my current code:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"type CONTAINS %#", [NSString stringWithFormat:#",%#,",typeBar]];
Simply put, this is what I want to achieve:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#",type, CONTAINS %#", [NSString stringWithFormat:#",%#,",typeBar]];
With the commas around the 'type'. But obviously when I do it like this, it doesn't recognise the attribute anymore. I have tried ,'type', but that doesn't work either.
I have tried this:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#",%K, CONTAINS %#", #"type", [NSString stringWithFormat:#",%#,",typeBar]];
But I get a: 'NSInvalidArgumentException', reason: 'Unable to parse the format string ",%K, CONTAINS %#"'
I hope the question was clear enough.
Thanks a lot!
You can't do that. What you're trying is attempting to change the key to be queried to something invalid, hence the parse error.
You can't really do what you want to if you just store a plain type. Presumably your type may be a string list of things?
So, you need to rethink your approach, there are a number of options depending on what your underlying problem is:
Store the text for type with commas at the beginning and end (as well as between items)
Don't use a string, use some other entity (if you have a list of items)
Predicate on the plain type, without the commas, and then filter the results in more detail once you have the items out of Core Data (where predicates are restrictive compared to 'full' code access to the strings)

Objective C Predicate of an array using a range of numbers

So I have an nspredicate that I am using to filter values in a json array. Instead of just searching for one value, I would like the predicate to find objects that are within a range of numbers such as 0-1.0 or 1.1-2.0.
This is my current predicate:
NSPredicate *Predicate = [NSPredicate predicateWithFormat:#"Engine CONTAINS %#", #"?"];
It is probably a simple solution but I have yet to find an answer to this. Thank you for your time.
Please read documentation: https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSPredicate_Class/Reference/NSPredicate.html
You can use conditions like AND and OR as well as inequality operations < and >
There are plenty of ways to do this and the docs have a few examples - see the Basic Comparisons section.
A basic solution would probably look like this
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"Engine BETWEEN %#", #[ #0, #1]];

Objective-C - how to find objects with prop equal to date?

I'm trying to filter an array of objects selecting those where a property of type NSDate are equal to a specific date. However, I only want to compare the date and ignore any time difference.
So I'm using an NSPredicate as so:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"PropertyWithDate == %#",
[NSDate date]];
As both the object is storing the date with time and the value for now I am passing holds both. How do I arrange it to ignore the time part in both during the comparison?
Remember, an NSDate expresses a point in time, independent of timezones, so it only makes sense to talk about the date part of an NSDate with respect to a specific calendar and timezone.
The short answer is that you'll need to construct two NSDates: one for 12am the day of your date, and one for 12am the day after, and then set up your predicate to look for dates between the two:
NSPredicate *firstPredicate = [NSPredicate predicateWithFormat:#"PropertyWithDate > %#", firstDate];
NSPredicate *secondPredicate = [NSPredicate predicateWithFormat:#"PropertyWithDate < %#", secondDate];
NSPredicate *predicate = [NSCompoundPredicate andPredicateWithSubPredicates:[NSArray arrayWithObjects:firstPredicate, secondPredicate, nil]];
To construct firstDate and secondDate, you'll want to use NSCalendar's components:fromDate: and dateFromComponents: methods. If you want to use the device's current default calendar and timezone, you can just use [NSCalendar calendar] to get a calendar to use.
You should use NSDatecomponents to do the compare. There is a great video about date math and the libraries in the WWDC 2011 videos.
See the NSDateComponents class reference for more information.

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.