I got problem with NSPredicate.
I got some engine where the predicates are constructed from a definition coming from a web service.
I use somethning like that:
[NSPredicate predicateWithFormat:#"max({1,2,3})==3"]
it's great, but I need max function for NSDate objects.
Here http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSExpression_Class/Reference/NSExpression.html#//apple_ref/doc/uid/TP30001190 is wrote that max() function can get only array containings objects representing numbers.
Is there any solution to use this function for that (override some method, create some category for NSPredicate)
Thanks in advance.
OK, this is possible, but it's weird. We'll have to build the format string programmatically. Alternatively, we could built it all by creating the individual NSExpression objects, but that'd be a lot more code (albeit likely safer, but whatever).
NSDate *maxDate = ...;
NSArray *arrayOfDates = ...; // an NSArray of NSDate objects
NSMutableArray *dateFormats = [NSMutableArray array];
for (NSDate *date in arrayOfDates) {
NSString *format = [NSString stringWithFormat:#"%f", [date timeIntervalSinceReferenceDate]];
[dateFormats addObject:format];
}
NSString *dateFormatString = [dateFormats componentsJoinedByString:#","];
NSString *format = [NSString stringWithFormat:#"CAST(max({%#}) = %%#, 'NSDate')", dateFormatString];
// format now looks like CAST(max({xxxx.xxxx, nnnn.nnnn, aaaa.aaaa, ....}), 'NSDate') = %#
NSPredicate *datePredicate = [NSPredicate predicateWithFormat:format, maxDate];
The trick here is to use the date's absolute time interval since the reference date as the arguments to the max() function, and then turn the result of that back into a date by using CAST(). More info on how to use the CAST() function with dates can be found in this blog post.
This solutions is realy good, but don't solve my problem.
I got something like this:
NSPredicate* predicate = [NSPredicate predicateWithFormat: condition];
[predicate evaluateWithObject:nil substitutionVariables: currentForm];
And condition can be:
max($widgetWhichReturnDate.result.result,$widgetWhichReturnDate.result)
or
max($widgetWhichReturnInt.result,$widgetWhichReturnInt1.result)
what widget is used here depends from webservice.
So I don't like to use different functions for Date and int (or in the future maybe float, double etc)
Related
I have an NSArray of NSDictionary objects. I want to filter the array based on keys of the dictionaries using NSPredicate. I have been doing something like this:
NSString *predicateString = [NSString stringWithFormat:#"%# == '%#'", key, value];
NSPredicate *predicate = [NSPredicate predicateWithFormat:predicateString];
NSArray *filteredResults = [allResultsArray filteredArrayUsingPredicate:predicate];
This works fine if they key passed in is one-word: Color, Name, Age. But it doesn't work if the key is multi-word, like: Person Age, Person Name.
Basically, any key that contains a space, it doesn't work. I have tried putting single quotes around the key in the string, just like they are done on the value side but that didn't work either. Also tried double quotes, but to no avail.
Please advise on this. Thanks in advance.
For me, Kevin's answer did not work. I used:
NSPredicate *predicateString = [NSPredicate predicateWithFormat:#"%K contains[cd] %#", keySelected, text];//keySelected is NSString itself
NSLog(#"predicate %#",predicateString);
filteredArray = [NSMutableArray arrayWithArray:[YourArrayNeedToFilter filteredArrayUsingPredicate:predicateString]];
When using a dynamic key, you should use the %K token instead of %#. You also don't want the quotes around the value token. They will cause your predicate to test for equality against the literal string #"%#" instead of against value.
NSString *predicateString = [NSString stringWithFormat:#"%K == %#", key, value];
This is documented in the Predicate Format String Syntax guide.
Edit: As Anum Amin points out, +[NSString stringWithFormat:] doesn't handle predicate formats. You want [NSPredicate predicateWithFormat:#"%K == %#", key, value] instead.
I'm using the following code to sort the results in a UIPicker. The results are coming randomly because I am using a dictionary / plist to store the data.
NSArray *components = [self.dobRangesDict allKeys];
NSArray *sorted = [components sortedArrayUsingSelector: #selector(compare:)];
self.dates = sorted;
this sorts the data, but I'm looking to sort the data numerically. Any ideas how I might go about accomplishing this?
thanks for any help.
There are a couple of options here.
My personal choice is to use [components sortedArrayUsingComparator:^(NSString *a, NSString *b) { return [a compare:b options:NSNumericSearch]; }]
I have a list of data that is a schedule. Each item has a time that it takes place in. I want to detect the current day and time and then display what items are available during that time. All I really know is how to get todays date and time and that I need to create a method to look in my data at what is currently "playing". Any suggestions?
By assuming that your schedule items are stored in an NSArray called scheduleItems and that these items have a date property, you could filter them with a predicate:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"date == %#",
[NSDate date]];
NSArray *todaysItems = [scheduleItems filteredArrayUsingPredicate:predicate];
The problem is that this will give you every item where its date is exactly now. You probably want to compare the date in a range:
NSDate *today = ...;
NSDate *tomorrow = ...;
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"date BETWEEN %#",
[NSArray arrayWithObjects:today, tomorrow,nil]];
NSArray *todaysItems = [scheduleItems fileteredArrayUsingPredicate:predicate];
Note: this is not tested.
I've got a Core Data model set up, with two entities in a one-to-many relationship (Items, and for each item, there can be multiple ResetDates). I'm pretty confident the model is set up correctly.
I can add new Items, and when doing so, add a new ResetDate (using the current date, with [NSDate date]). I can retrieve and display Items. What I'm having trouble with is retrieving and displaying the ResetDates.
Updated: It works now, thanks very much to the answerers below. Here's the code in question:
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"resetDate" ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:&sortDescriptor count:1];
NSMutableArray *sortedResets = [[NSMutableArray alloc] initWithArray:[item.resets allObjects]];
[sortedResets sortUsingDescriptors:sortDescriptors];
NSDate *oldDate = [[sortedResets lastObject] resetDate];
if ( !oldDate ) {
oldDate = [NSDate date];
}
NSInteger numberOfDays = [self timeIntervalWithStartDate:oldDate withEndDate:currentDate]; // This function works fine, when given two NSDate objects
daysSinceLabel.text = [NSString stringWithFormat:#"%d days", numberOfDays];
First, NSArray -objectAtIndex: is not returning nil if you pass it an index that is out of the bounds, it will raise an NSRangeException, when you're not sure about the index, and need to use -objectAtIndex:, you have to call the -count method before to check.
More importantly, an NSArray can't contain a nil value, as nil is not an object.
Then, no, it's not an NSDate object, when you ask item for it's resets relationship (item.resets), you get an NSSet that contain Reset managed objects in return, not NSDate objects, what you want is the resetDate attribute of the returned Reset managed objects, maybe something like this :
// NSArray -lastObject method return nil if the array is empty
// Sending messages to nil is Ok there, so we can call resetDate directly
NSDate *oldDate = [[sortedResets lastObject] resetDate];
if ( !oldDate ) {
oldDate = [NSDate date];
}
Hope that help, and that my English is understandable...
Maybe replacing :
NSDate *oldDate = sortedResets[0];
with :
NSDate *oldDate = [sortedResets objectAtIndex:0];
will help. sortedResets is an NSArray object, not a C array ;)
I have a Core Data model in which a Task entity includes an optional to-many relationship ExcludedDays to the ExcludedDay entity. One of the properties of ExcludedDay is day, which is an NSDate object. The ExcludedDay entity has an inverse mandatory to-one relationship to the Task entity.
In order to fetch the tasks for a specified day, I need to make sure that the specified day does not appear as the day property of any ExludedDay entity.
I started trying
NSPredicate *dayIsNotExcludedPredicate = [NSPredicate predicateWithFormat: #"ALL excludedDays.day != %#", today];
However, despite what the documentation says, ALL does not work and the application throws an exception: Terminating app due to uncaught exception ‘NSInvalidArgumentException’, reason: ‘Unsupported predicate.
After posting the same question in this forum, I was able to devise the following predicate with the help of various people:
NSPredicate * dayIsNotExcludedPredicate = [NSPredicate predicateWithFormat: #"excludedDays.#count == 0 || (excludedDays.#count > 0 && NONE excludedDays.day == %#))", today];
While this worked at first, I have just discovered that this only works when the ExcludedDay entity contains ONLY one day. As soon as the ExcludedDay entity contains more than one day for the same task, this predicate stops working. As a result, a task is selected for a day even though the day appears as a day in the ExcludedDay entity, which is of course wrong. The problem is not tied to the property day being a NSDate object: replacing day with the corresponding NSString or equivalently with an integer, I still face the same issue and incorrect behaviour.
What is the correct way to implement the predicate in this case? May this be a bug related to the ANY aggregate operator when using core data?
Thank you in advance, this is now driving me crazy.
It turns out this is yet another problem with missing and/or inconsistent documentation.
The correct predicate in this case is the following one:
[NSPredicate predicateWithFormat:#"(excludedOccurrences.#count == 0) OR (0 == SUBQUERY(excludedOccurrences, $sub, $sub.day == %#).#count)", today]
In the predicate, a subquery is used to test if the number of related excludedOccurrences with a date matching your test date is equal to zero. However, the documentation is misleading. Here is what the Predicate Programming Guide says regarding the use of predicates in conjunction with to-many relationships.
Using Predicates with Relationships
If you use a to-many relationship, the construction of a predicate is slightly different. If you want to fetch Departments in which at least one of the employees has the first name "Matthew," for instance, you use an ANY operator as shown in the following example:
NSPredicate *predicate = [NSPredicate predicateWithFormat:
#"ANY employees.firstName like 'Matthew'"];
If you want to find Departments in which all the employees are paid more than a certain amount, you use an ALL operator as shown in the following example:
float salary = ... ;
NSPredicate *predicate = [NSPredicate predicateWithFormat:
#"ALL employees.salary > %f", salary];
Quite curious how to solve this I setup a little project and created the context you are using.
NSDate *today = [NSDate date];
NSMutableArray *objects = [NSMutableArray array];
{
NSArray *day = [NSArray arrayWithObjects:today, [today dateByAddingTimeInterval:20.0f], nil];
NSDictionary *excludedDay = [NSDictionary dictionaryWithObject:day forKey:#"day"];
NSDictionary *object = [NSDictionary dictionaryWithObject:excludedDay forKey:#"excludedDay"];
[objects addObject:object];
}
{
NSArray *day = [NSArray arrayWithObjects:[today dateByAddingTimeInterval:20.0f], nil];
NSDictionary *excludedDay = [NSDictionary dictionaryWithObject:day forKey:#"day"];
NSDictionary *object = [NSDictionary dictionaryWithObject:excludedDay forKey:#"excludedDay"];
[objects addObject:object];
}
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"NONE excludedDay.day == %#", today];
NSArray *filtered = [objects filteredArrayUsingPredicate:predicate];
NSLog(#"%#", filtered);
This gives the object when:
The day array is empty
The day array does not contain the 'today' date
This does not give the object when:
The day array contains the 'today'
It doesn't matter how many objects in the day array are