NSPredicate with Multiple Conditions - ios

I am trying to create a NSPredicate with multiple conditions. I've found several solutions, but none of them appear to be working with my method. The best looking one I've found is below.
This is my single predicate method, and it works just fine:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"name contains[c] %#",
searchText];
filteredBusinesses = [businesses filteredArrayUsingPredicate:predicate];
Here is my edited version with multiple conditions. I'm not sure whats going wrong. Any ideas?
NSPredicate *p1 = [NSPredicate predicateWithFormat:#"name contains[c] %#", searchText];
NSPredicate *p2 = [NSPredicate predicateWithFormat:#"businessArea contains[c] %#",
searchText];
NSPredicate *predicate = [NSCompoundPredicate andPredicateWithSubpredicates:#[p1, p2]];
filteredBusinesses = [businesses filteredArrayUsingPredicate:predicate];

You can try this
NSPredicate *predicate = [NSCompoundPredicate orPredicateWithSubpredicates:#[p1, p2]];

Addition to #Nikunj's answer, you can also use NSCompoundPredicate for your AND operations like this.
Obj-C - AND
NSPredicate *predicate1 = [NSPredicate predicateWithFormat:#"X == 1"];
NSPredicate *predicate2 = [NSPredicate predicateWithFormat:#"X == 2"];
NSPredicate *predicate = [NSCompoundPredicate andPredicateWithSubpredicates:#[predicate1, predicate2]];
Swift - AND
let predicate1:NSPredicate = NSPredicate(format: "X == 1")
let predicate2:NSPredicate = NSPredicate(format: "Y == 2")
let predicate:NSPredicate = NSCompoundPredicate(andPredicateWithSubpredicates: [predicate1,predicate2] )
Swift 3 - AND
let predicate1 = NSPredicate(format: "X == 1")
let predicate2 = NSPredicate(format: "Y == 2")
let predicateCompound = NSCompoundPredicate.init(type: .and, subpredicates: [predicate1,predicate2])

Nothing looks wrong in the code you posted, which means the error is probably coming from when you evaluate the predicate by filtering the array.
Since the first predicate works, the problem lies with the businessArea key path.
Filtering the array would throw an exception if:
There's an object in the array that doesn't have a businessArea value (as in, it's not an object that has a -businessArea method)
The object does have a businessArea value, but the value is neither an NSString nor nil

NSCompoundPredicate received a facelift. Just in case someone's looking for a Swift 5+ version:
AND
let predicatesAND = NSCompoundPredicate(andPredicateWithSubpredicates:[pred1,pred2])
OR
let predicatesOR = NSCompoundPredicate(orPredicateWithSubpredicates:[pred1,pred2])

First off, I suspect you may have a flaw in your two basic predicates. Judging by your object name "businesses", it is a collection i.e NSArray, NSSet or NSDictionay of items. In that case, "name contains[c] 'mmm'" has no meaning when evaluated for the whole collection, and should crash. It is missing a collection operator (ANY, ALL, SOME) , e.g. "ANY name contains[c] 'mmm'".
Ah... now I see... you're using the predicate to filter an NSArray, which means the predicate is executed on each item separately. So your predicates are alright.
Now... the simplest way to combine your predicates is at the very format-string level! what's wrong with
NSPredicate *p1 = [NSPredicate predicateWithFormat:#"name contains[c] %# AND businessArea contains[c] %#", nameToSearch, areaToSearch];
If you insist on doing it the hard way, than building your compound NSPredicate should have worked too - only you employed the same searchText to both conditions, which may caused the predicate to never match.
My advice to you, place a breakpoint on the line evaluating the filtered array and stop just before the filtering, and in the debugger print the predicate description, using the debugger command po p1 you will see there what is the final form of your predicate, and will be able to determine if you built it correctly.

Related

How to write a nspredicate to get records that contains part of the search term?

How do i write a predicate to filter out data with a search string like this.
Users enters "red pepper".
This should return all the records including, "red pepper", "red bell pepper" etc
This is what i have written.
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"(ingredientName contains[c] %#)" ,self.searchIngrediants.text ];
[fetchRequest setPredicate:predicate];
This only returns records that contains the search term such as "red pepper", "belgian red pepper".
Is it possible to do this with a predicate. Im using Objective-c.
Any help would be much appreciated!
For that you need to use LIKE.
NSString *searchStringWithPattern = [self.searchIngrediants.text stringByReplacingOccurrencesOfString:#" " withString:#"*"];
searchStringWithPattern = [NSString stringWithFormat:#"*%#*", searchStringWithPattern];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"(ingredientName LIKE[cd] %#)" , searchStringWithPattern];
[fetchRequest setPredicate:predicate];
Objective C Code:-
NSPredicate *predicate;
predicate = [NSPredicate predicateWithFormat:#"ingredientName contains[cd] %#",self.searchIngrediants.text];
Contains will check the character is there or not.
Swift Code :-
var predicate: NSPredicate?
predicate = NSPredicate(format: "ingredientName contains[cd] %#", searchIngrediants.text)
With regular expression it should be something like:
NSString* regex = #"red.*pepper";
NSPredicate* predicate = [NSPredicate predicateWithFormat:#"(ingredientName MATCHES %#)", regex];
If your input has newlines/TABs/... adapt the regex accordingly.

Filtering values with NSNumber == nil in NSPredicate

I'm trying select values by property with type NSNumber, if property==intValue or property==nil.
But this code ignores values with property==nil.
What the point?
NSPredicate *predicate = [NSPredicate predicateWithFormat:
#"(ANY region.beacons.major == %d OR ANY region.beacons.major = nil)
AND (ANY region.beacons.minor == %d OR ANY region.beacons.minor = nil)",
rangedBeacon.major.intValue, rangedBeacon.minor.intValue];
UPD: The most simple doesn't work too:
NSPredicate *predicate = [NSPredicate predicateWithFormat:
#"ANY region.beacons.minor = nil"];
UPD 2: Probably problem because of ANY+nil
According to this answer Core Data, NSPredicate, ANY key.path == nil ANY select only NOTNULL values.
Simply test for one of the properties of minor; for example:
NSPredicate *predicate = [NSPredicate predicateWithFormat:
#"region.beacons.minor.length = 0"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:
#"region.beacons.minor.stringValue = nil"];
EDIT I recognized, that you should remove the ANY
If minor is nil, any call would return in nil as well, or 0 for numeric types.
For one thing, I notice you're using the assignment operator = instead of equality == while comparing with nil.
Also, try comparing with the Singleton NSNull instance [NSNull null].

Compound Predicate doesn't work

I have an array of objects with name and address.
Both properties are of type NSString.
But when i'm using the compound predicate with both results i don't get any result. If i'm using just one predicate it works very well.
Has anyone an idea whats going wrong here in my code? thnx
-(void)filterContentForSearchString:(NSString*) searchText
{
NSPredicate *resultsPredicate = [NSPredicate predicateWithFormat:#"SELF.name like[cd] %#", searchText];
NSPredicate *resultsPredicate2 = [NSPredicate predicateWithFormat:#"SELF.place like[cd] %#", searchText];
NSPredicate *compoundpred = [NSCompoundPredicate andPredicateWithSubpredicates:#[resultsPredicate, resultsPredicate2]];
self.searchResults = [self.companies filteredArrayUsingPredicate:compoundpred];
}
Can you try using a single predicate declaration
NSPredicate* predicate = [NSPredicate predicateWithFormat:#"SELF.name like[cd] %# OR SELF.place like[cd] %#", searchText, searchText];
and filter the array with it directly
self.searchResults = [self.companies filteredArrayUsingPredicate:predicate];

NSPredicate too many arguments. Hard to read it

I just faced with some issue that is connected to clear code. I use predicates in many modules of my projects. When I work with core data the NSPredicates are really helpful for retrieving filtered object.
But the predicating is good for logic but not for code readability as I use it.
So let's see on this example:
NSPredicate *predicate = [NSPredicate predicateWithFormat:
#"(ANY option.playerzoneID == %d) \
AND (ANY option.gameId == %#) \
AND (team.teamID == %#) \
AND (league.leagueID == %#) \",
zoneIdType,
SELECTED_GAME.gameID,
SELECTED_TEAM.teamID,
SELECTED_LEAGUE.leagueID];
When I look on this predicate I am little confused about this structure even in case that this predicate I have written one week ago.
Any suggestion how to make this code more readable?
I think it will be better to have something like this:
[predicate setParametr:#"gameID == %#", SELECTED_GAME.gameID];
[predicate setParametr:#"league.leagueID == %#", SELECTED_LEAGUE.leagueID];
You can explore the following:
+ (NSPredicate *)andPredicateWithSubpredicates:(NSArray *)subpredicates
+ (NSPredicate *)orPredicateWithSubpredicates:(NSArray *)subpredicates
+ (NSPredicate *)notPredicateWithSubpredicate:(NSArray *)subpredicate
With this you can actually form short predicates and join them together with arrays. So, you don't exactly get what you wished for, but its a near shot.
NSPredicate *predicate1 = [NSPredicate predicateWithFormat:#"(ANY option.playerzoneID == %d)", zoneIdType];
NSPredicate *predicate2 = [NSPredicate predicateWithFormat:#"(ANY option.gameId == %d)", SELECTED_GAME.gameID];
NSPredicate *predicate3 = [NSPredicate predicateWithFormat:#"(ANY team.teamID == %#)", SELECTED_TEAM.teamID];
NSCompoundPredicate *compoundANDPredicate = [NSCompoundPredicate andPredicateWithSubpredicates:#[predicate1, predicate2, predicate3]];
Read more about these here:http://nshipster.com/nspredicate/
Edit: Using OR Predicates along with AND Predicates:
Let's assume: you have this predicate to add as OR:
NSPredicate *predicate4 = [NSPredicate predicateWithFormat:#"(ANY team.teamName == %#)", SELECTED_TEAM.teamName];
So, you can group your AND and OR predicates into one compound predicate (compoundANDPredicate as shown above) and then use
+ (NSPredicate *)orPredicateWithSubpredicates:(NSArray *)subpredicates
So it becomes:
[NSCompoundPredicate orPredicateWithSubpredicates:#[compoundANDPredicate, predicate4]];

NSPredicate - Dynamic predicate with arguments

I am new to NSPredicates, but I know the basics.
This is how my predicate looks now:
[NSPredicate predicateWithFormat:#"name contains[c] %#", searchText];
What I want to do, is to be able to create a predicate like this (Pseudo code):
NSPredicate *myPredicate = [NSPredicate predicateWithBaseString:#"name contains[c] _ARGUMENT_"];
And then use it in, for example a loop (pseudo code):
for(NSString *searchString in self.allStrings)
{
NSPredicate *myNewPredicate = [myPredicate predicateWithArgument:searchString];
//Here I do the searchOperations with my new predicate
}
I want to do this so that I can have a few base predicates and then put parameters on them for searching. I do not want to create my predicate again.
Hopefully you understand.
Thank you!
Use following
NSPredicate *myPredicate = [NSPredicate predicateWithFormat:#"name contains[c] $ARGUMENT"];
for(NSString *searchString in self.allStrings)
{
NSPredicate *myNewPredicate = [myPredicate predicateWithSubstitutionVariables:#{#"ARGUMENT" :searchString}];
//Here I do the searchOperations with my new predicate
}
also I suggest to read this article http://nshipster.com/nspredicate/
It seems as if i just found the answer. I can use predicateWithSubstitutionVariables like this:
NSPredicate *resultPredicate = [NSPredicate predicateWithFormat:#"name contains[c] $searchText"];
NSDictionary *sub = [NSDictionary dictionaryWithObject:searchText forKey:#"searchText"];
NSPredicate *filter = [resultPredicate predicateWithSubstitutionVariables:sub];

Resources