NSPredicate Creation - ios

how shall I create an NSPredicate to that test whether a NSString start with some pattern, say "AAA"?
Thank you very much!
I tried to read Apple's reference but could not understand it.

Like this:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF BEGINSWITH[cd] %#", #"AAA"];
And if you want to find all matching strings from array:
for(NSString *n in [myArray filteredArrayUsingPredicate:predicate]) {
NSLog(#"%#", n);
}
And answer to your comment about 'c' and 'd' characters etc:
SELF is in this case NSString object
BEGINSWITH[] explanation not needed, I think.
"String comparisons are by default case and diacritic sensitive. You can modify an operator using the key characters c and d within square braces to specify case and diacritic insensitivity respectively" (quote from Apple's Predicates Programming Guide)

You can try this one:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"name CONTAINS[cd] %#", Your_text];

Related

Obj-C - NSPredicate - Can I implement an 'or IF' statement?

I'm trying to write a filter with NSPredicate that states:
IF field_Uid contains 47 and field_Target contains 202...display these items
This portion works great. However, I also want to show items in which the reverse is true, eg.:
OR IF fieldUid contains 202 and fieldTarget contains 47...display these
items also
Right now, I have the following code:
NSString *targetedUser = #"47";
NSString *myID = #"202";
NSPredicate *p1 = [NSPredicate predicateWithFormat:#"fieldUid contains[cd] %#", targetedUser];
NSPredicate *p2 = [NSPredicate predicateWithFormat:#"fieldTarget contains[cd] %#", myID];
//E.G. See p3 & p4 being the reverse?
// NSPredicate *p3 = [NSPredicate predicateWithFormat:#"fieldUid contains[cd] %#", myID];
// NSPredicate *p4 = [NSPredicate predicateWithFormat:#"fieldTarget contains[cd] %#", targetedUser];
NSCompoundPredicate *p = [NSCompoundPredicate andPredicateWithSubpredicates:#[p1, p2/*, p3, p4*/]];
NSArray *filtered = [self.messages filteredArrayUsingPredicate:p];
How would I write in the latter? I'm not sure how to do it without making the statement requiring all 4 lines to be true? (See commented out code - I've left it in so you can see what I mean...)
Just combine two compound "and" predicates using a compound "or" predicate.
You have the first compound "and" predicate in your question. Create the 2nd compound "and" predicate as needed.
Now use [NSCompoundPredicate orPredicateWithSubpredicates:] passing in the two "and" compound predicates.
you can also use
NSString *targetedUser = #"47";
NSString *myID = #"202";
//this will easily fix your issue, simply update your query like this
NSPredicate *p = [NSPredicate predicateWithFormat:#"(fieldUid contains[cd] %# AND fieldTarget contains[cd] %#) OR (fieldUid contains[cd] %# AND fieldTarget contains[cd] %#)", targetedUser, myID, myID, targetedUser];
NSArray *filtered = [self.messages filteredArrayUsingPredicate:p];

use NSPredicate to filter object

Here say I have a array of objects with two attributes:
// array of object
NSArray *objects
// object
NSString *primaryTag;
NSArray *secondaryTag;
Since what I want is when the this object contains the givenTag, it could be passed to a new array called results;
Here is my codes:
NSPredicate *resultPredicate = [NSPredicate predicateWithFormat:#"primaryTag == %# || secondaryTag CONTAINS[c] %#", givenTag, givenTag];
results = [objects filteredArrayUsingPredicate:resultPredicate];
It seems that the primaryTag works well, but the secondaryTag doesn't work, can someone help me out. I am not that familiar with NSPredicate filtering. Thanks in advance.
The most efficient way to do that is with a NSCompoundPredicate like so:
NSArray *subPredicates = #[tag1, tag2, tag3];
NSPredicate *compoundPredicate = [NSCompoundPredicate orPredicateWithSubpredicates:subPredicates];
Your question is a little unclear so you might also want:
andPredicateWithSubpredicates
Depending on exactly what the nature of the result set you are looking for.
See Apple Docs here: NSCompoundPredicate Docs
i implemented the following custom class:
#interface CustomObject : NSObject
#property (copy, nonatomic) NSString *primaryTag;
#property (strong, nonatomic) NSArray *secondaryTag;
#end
and overrode it's description method for the NSLog statement to print something we understand:
- (NSString *)description {
return [NSString stringWithFormat:#"primaryTag: %#, secondaryTag: %#", _primaryTag, [_secondaryTag componentsJoinedByString:#", "]];
}
then i created some objects from the custom class and added them to an array:
NSMutableArray *objects = [NSMutableArray array];
CustomObject *obj1 = [CustomObject new];
obj1.primaryTag = #"stringToSearchFor";
obj1.secondaryTag = #[#"notTheStringToSearchFor", #"somethingElse"];
[objects addObject:obj1];
CustomObject *obj2 = [CustomObject new];
obj2.primaryTag = #"differentString";
obj2.secondaryTag = #[#"nothingWeAreLookingFor"];
[objects addObject:obj2];
CustomObject *obj3 = [CustomObject new];
obj3.primaryTag = #"anotherOne";
obj3.secondaryTag = #[#"whoCaresForThisString", #"stringToSearchFor"];
[objects addObject:obj3];
finally i created a string to search for and the predicate:
NSString *givenTag = #"stringToSearchFor";
NSPredicate *resultPredicate = [NSPredicate predicateWithFormat:#"primaryTag == %# || secondaryTag CONTAINS[c] %#", givenTag, givenTag];
when i log out the result i get the correct results:
NSLog(#"%#", [objects filteredArrayUsingPredicate:resultPredicate]);
logs:
(
"primaryTag: stringToSearchFor, secondaryTag: notTheStringToSearchFor, somethingElse",
"primaryTag: anotherOne, secondaryTag: whoCaresForThisString, stringToSearchFor"
)
which is obj1 and obj3. correct! if it does not work for you there's gotta be something else wrong with your code...
If my understanding of the original question is incorrect, please let me know, and I will adjust my answer.
Problem: You have an array of objects with 2 properties. One is primaryTag, which is a string. The second is an array of secondaryTags, which is a collection of strings. You want to filter all objects where either the primaryTag matches, or where the search string matches one of the secondaryTags.
Answer The proper way to match strings is via MATCHES or CONTAINS.
NSPredicate *pPredicate =
[NSPredicate predicateWithFormat:#"%K CONTAINS[cd] %#",
#"primaryTag", searchString];
NSPredicate *sPredicate =
[NSPredicate
predicateWithFormat:#"SUBQUERY(%K, $st, $st CONTAINS[cd] %#).#count > 0",
#"secondaryTags", searchString];
NSCompoundPredicate *searchPredicate =
[NSCompoundPredicate orPredicateWithSubPredicates:#[ pPredicate, sPredicate ]];
How it works: The first predicate is a straightforward match. You can replace CONTAINS with MATCHES, if that better fits the kind of comparison you wish to make. The [cd] suffix means case-insensitive and diacritic-insensitive. It's normal to include those when searching/filtering, but again, it's up to you. Instead of embedding the property name in the predicate format string, I use %K and a replacement parameter. In production code, that replacement parameter would be a constant.
The second predicate is a little trickier. It uses a SUBQUERY() to filter the secondaryTags array, and returns the object as matching if at least one secondary tag matches the search string. SUBQUERY() is a function with 3 parameters. The first is the collection being searched. The second is a temporary variable that represents each item in the collection, in turn; it is used in the 3rd parameter. The 3rd parameter is a regular predicate. Each item in the collection that matches the filter is included in the output of SUBQUERY(). At the end, the matching secondary tags are counted (via #count), and if the count is greater than zero, the original object is considered to have matched, so will be included in the filtered output.
Finally, we combine these two predicates into one searchPredicate, which can now be used to filter your array of objects.
I seen this issue,
My normal approch is to use the NSPredicate twice,
So that I can track the result at every steps:
Option 1:
NSPredicate *resultPredicate1 = [NSPredicate predicateWithFormat:#"primaryTag == %#", givenTag];
results1 = [objects filteredArrayUsingPredicate:resultPredicate1];
NSPredicate *resultPredicate2 = [NSPredicate predicateWithFormat:#"secondaryTag CONTAINS[c] %#", givenTag];
finalResults = [results1 filteredArrayUsingPredicate:resultPredicate2];
Option 2:
Use NSCompoundPredicate to compound multiple filtering. You can easily find many examples on google and stackOverFlow.
Hope this will help,
Thanks

Predicate for range of NString in a Array

I have an array(NSArray) let's say
NSArray *cityArry = #[#"Chennai", #"Mumbai", #"Kolkata"];
For example I have mentioned the NSArray with 3 objects, but actually in runtime I have an array which contain more than 1000 objects.
Now I have the string(NSString) "chennai Tnagar"
NSString *scanedStr = #"Chennai Tnagar";
So, now I want to find out that the whether range of scanedStr contain in cityArry. For example I should get output as #"Chennai" from the cityArry.
I know we can do this by looping the NSArray and by using rangeofstring we can get the output. Since I have more than 1000 objects I don't want to do the looping. Please guide me and help me out how to solve this.
Thanks in advance
You could basically split the string and search for the array on the cityArry.
NSString *scannedStr = #"Chennai Tnager";
NSPredicate *predicate = [NSPredicate predicateWithFormat: #"SELF in[cd] %#", [scannedStr componentsSeparatedByString:#" "]];
[cityArray filteredArrayUsingPredicate: predicate];
I am certain that this is a duplicate of another answer, but I can't find it. If anybody can, please close this as a dupe.
Anyway, the IN operator works nicely on strings. Apparently it is undocumented, so you will have to decide if you want to rely on it:
NSArray *cityArray = #[#"Chennai",#"Mumbai",#"Kolkata"];
NSString *scannedStr = #"Chennai Tnagar";
NSPredicate *pred = [NSPredicate predicateWithFormat: #"SELF IN[cd] %#", scannedStr];
NSArray *result = [cityArray filteredArrayUsingPredicate: pred];
This solution has the advantage of keeping the object of the predicate on the left, which I happen to find easier to read - but as mentioned, it is not documented.
In case you wish to use a documented construct, you can swap the order of the predicate and use a normal CONTAINS:
pred = [NSPredicate predicateWithFormat: #"%# CONTAINS[cd] SELF", scannedStr];
I propose that you make use of SQL query like by using sqlite3 as a database of the list of all cities you have. Its a bit painful implementing a database but it solves the problem I think.

NSPredicate and BeginsWith

I would like to figure out the NSPredicate that will search my Core data for words that begins with:
For example:
description field in the core data has text like this:
My name is Mike
My name is Moe
My name is Peter
My name is George
If I search 'My name is' I need to get the 3 lines
If I search 'My name is M' I need to get the first 2 lines
I tried the code below, but can't get what I need. My guess I need a regular expression, but not sure how to do it.
[NSPredicate predicateWithFormat:#"desc beginswith [cd] %#",word];
I did it this way recently and worked fine. Try it out. I see I don't have [cd] and beginswith but contains. You could give it a try.
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF contains %#", textField.text];
NSArray *list = [allWords filteredArrayUsingPredicate:predicate];
for(NSString *word in list){
NSLog(#"%#",word);
}
After reading the comments on the starting post:
First of all, you should split up the words in the string from the textfield:
NSArray *myWords = [textField.text componentsSeparatedByString:#" "];
Then doing the same like you first would:
for(NSString *wordFromTextField in myWords){
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF contains %#", wordFromTextField];
NSArray *list = [allWords filteredArrayUsingPredicate:predicate];
for(NSString *word in list){
NSLog(#"%#",word);
}
}
Instead of NSLogging the words you could add them to an array of course.

NSPredicate contains search in a string that contains number and letters

I have to use NSPredicatefor search through some Core Data objects. But the key nameof the custom object could contains both numbers then letters.
The string could look like:John 1234 Lennonor Ringo Starr.
I normally would use the predicate NSPredicate *predicate = [NSPredicate predicateWithFormat:#"Any name CONTAINS[cd] %#",searchString];
But, if I search for John Lennon the predicate don't return anything, because it can't compare if it contains the characters John Lennon, since is missing the 1234. Any tips what kind of predicate I can use?
You can tokenise your query , maybe as simple as
NSArray *tokens = [querystring componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
Then construct a compound predicate.
NSMutableArray *predarray = [NSMutableArray array];
for(NSString *token in tokens)
{
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"Any name CONTAINS[cd] %#",token];
[predarray addObject:predicate];
}
NSPredicate *final = [NSCompoundPredicate andPredicateWithSubpredicates:predarray];
And feed that to your query
In real life I would run a bit of validation against each token to check its going to make a valid predicate and wont crash or create a security risk. e.g Strip special chars like "* []"
EDIT:Corrected predicate type to work with questions situation.
Try using LIKE instead of contains, and then you can use wild cards, for instance John*Lennon should match a string that starts with John and ends with Lennon with any number of other characters in between. You can use ? Instead which would match only one character for each question mark if you want more control over what's matched.
You could split the search string up into an array of strings and then switch your predicate to look for any string in the name:
NSArray *strings = [searchString componentsSeparatedByString:#" "];
NSPredicate *pred = [NSPredicate predicateWithFormat:#"ANY %# IN name",strings];

Resources