NSPredicate too many arguments. Hard to read it - ios

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

Related

NSPredicate with OR returning error

I have a problem with the following predicate:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"(creatorUserRecordID == %#) OR (toUser == %#)", userId, userId];
When I use it in query CKQuery *query = [[CKQuery alloc] initWithRecordType:#"Message" predicate:predicate]; I have an error that says: 'CKException', reason: 'Unexpected expression'.
When I use these two seperately like this:
NSPredicate *predicate1 = [NSPredicate predicateWithFormat:#"(creatorUserRecordID == %#)", userId];
NSPredicate *predicate2 = [NSPredicate predicateWithFormat:#"(toUser == %#)", userId];
And then performing query with one of these predicates it works fine. I also tried using NSCompoundPredicate but the result is always the same... Any ideas?
The documentation for CKQuery lists all of the valid predicate syntax. Oddly, under "Basic compound predicates" it lists NOT, AND, and &&. OR and || are not listed and apparently are not supported for CloudKit query predicates.

NSPredicate with Multiple Conditions

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.

NSPredicate find an array inside db

I have the DB that contains an
NSString *Number
NSSet *Values
I can easily get all the results based on the string Number with the following:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"Number LIKE[c] %#", numberValue];
NSArray *result = [DB MR_findAllWithPredicate:predicate inContext:_context];
If instead of Number I want to pass an Array of values, how can I find in db all the results that contain the given array (NSSet)?
NSPredicate *predicate = [NSPredicate predicateWithFormat: ????
NSArray *result = [DB MR_findAllWithPredicate:predicate inContext:_context];
UPDATE
Based on Igoris answer :
[NSPredicate predicateWithFormat:#“(Values.#count == %d) AND (SUBQUERY(Values, $x, $x IN %#).#count == %d)", allValues.count, allValues, allValues.count];
This seem to work but it doesn t return all the values in result.
Just use IN:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"Number IN[c] %#", numberValues];
Where numberValues is NSArray containing numbers you are looking for
UPDATE:
After data structure clarifications I came up with the following predicate to get objects:
[NSPredicate predicateWithFormat:#“(Values.#count == %d) AND (SUBQUERY(Values, $x, $x IN %#).#count == %d)", allValues.count, allValues, allValues.count];
How about ANY and NONE?
[NSPredicate predicateWithFormat:#"ANY number == %#", numberValues];

I can't use two operator in predicatewithformat in same time

NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF MATCHES[cd] %# AND (NOT (SELF MATCHES[cd] %#))", [NSString stringWithFormat: #".*\\b%#.*", string],[NSString stringWithFormat: #".*\\[\\b%#.*", string]];
What is wrong in my code?
try with subquery function it's helpful you
[NSPredicate predicateWithFormat#"(add == %#) AND (SUBQUERY(subs, $x, $x.numbervalue == %# or $x.numbervalue == %#).#count > 0)", #"add",number1, number2];
Another:
AND & OR can be used in predicate format strings to create compound predicates. However, the same can be accomplished using an NSCompoundPredicate.
For example, the following predicates are equivalent:
[NSCompoundPredicate andPredicateWithSubpredicates:#[[NSPredicate predicateWithFormat:#"age > 25"], [NSPredicate predicateWithFormat:#"firstName = %#", #"Raja"]]];
[NSPredicate predicateWithFormat:#"(age > 25) AND (firstName = %#)", #"Raja"];
Source: Mattt Thompson

How to specify range with NSPredicate in Objective C

I want to query sqllite table by specifying a range. So it's like give me all records whose id column between 3000 and 3010.
I tried what Apple recommends, but it didn't work. Here is what I tried and failed.
NSPredicate *betweenPredicate =
[NSPredicate predicateWithFormat: #"attributeName BETWEEN %#", #[#1, #10]];
I have 2 strings called start and end. I updated Apple's example as following.
NSPredicate *betweenPredicate =
[NSPredicate predicateWithFormat: #"%# BETWEEN %#", columnName, #[start,end]];
When I executeFetchRequest with the above predicate I get 0 records even though the table has records matching the predicate. Can someone point where I go wrong?
You have to use the %K format specifier for attribute names:
[NSPredicate predicateWithFormat: #"%K BETWEEN %#", columnName, #[start,end]];
If that does not work (I have never used "BETWEEN" for Core Data fetch requests), you
could replace it by the equivalent predicate
[NSPredicate predicateWithFormat: #"%K >= %# AND %K <= %#",
columnName, start, columnName, end];

Resources