NSPredicate array contains question [duplicate] - ios

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Searching/Filtering a custom class array with a NSPredicate
I have a array that contains objects of a custom class, and I would like to filter the array based on if one of the classes attributes contains a custom string. I have a method that is passed the attribute that I want to be searched (column) and the string that it will search for (searchString). Here is the code I have:
NSPredicate *query = [NSPredicate predicateWithFormat:#"%# contains %#", column, searchString];
NSMutableArray *temp = [displayProviders mutableCopy];
[displayProviders release];
displayProviders = [[temp filteredArrayUsingPredicate:query] mutableCopy];
[temp release];
For some reason, this always returns an empty array. Three of the four columns are ints or doubles, so I could add an if statment to convert searchString to a number, if needed. However, even when searching the attribute that is a string, and should have some rows that contain the string searchString, an empty array is returned.
Am I using "contains" incorrectly?

For searching strings in a predicate, i've found that wrapping single quotation marks around what I am searching for works best and using contains[cd] rather than contains like below (the [cd] bit specifies a case & diacritic insensitive search):
NSPredicate *query = [NSPredicate predicateWithFormat:#"%# contains[cd] '%#'", column, searchString];
For searching a column with Integers, using double equals should work fine too:
NSPredicate *query = [NSPredicate predicateWithFormat:#"%# == %#", column, searchString];

I think your predicate should be [NSPredicate predicateWithFormat: #"column contains %#", searchString]
The way it is right now, your predicate becomes ""column" contains "string"", which always evaluates to false.
Using contains[cd] may also be a good idea depending on your situation.

Related

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

NSPredicate filtering an array based on a localised string entry

We have a main plist file, which includes our data source.
The above shows an example of one sub dictionary from the root array.
I would like to create an NSPredicate to search/filter the Category Name. But not the value in the plist as this is a localized string key. For instance when this is used we use:
cell.categoryNameLabel.text = NSLocalizedString([cellData objectForKey:#"CategoryName"], nil);
I have an NSPredicate that works on the Search Terms, but would also like that updated to look at localized string values instead as well is that is possible.
I have tried to convert it to an nslocalizedstring approach but it does not seem to work:
NSPredicate *namePredicate = [NSPredicate predicateWithFormat:#"(%K.%# LIKE[cd] %#)",#"CategoryName",NSLocalizedString(#"YELP_CAT_NAME_AIRPORTS", nil), searchText];
I think your problem is in the %K.%#, if you look at the final predicate I believe that it will put quotes around the %#, which is not what you want (guessing by the dot notation).
EDIT:
You could also use a predicate block:
NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
return [NSLocalizedString(evaluatedObject[#"CategoryName"],nil) isEqualToString:#"searchString"];
}];
EDIT 2:
The above predicate works by returning any row that matches the above criteria. To perform a more complicated search, just increase the number of requirements to return true. For example if you want to see if the localized string inside of the 'CategoryName' either contains a string or equals a string you could do something like this:
NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
NSString *localizedText = NSLocalizedString(evaluatedObject[#"CategoryName"],nil);
BOOL perfectMatch = [localizedText isEqualToString:#"searchString"];
BOOL containsString = [localizedText rangeOfString:#"searchString"].location !=NSNotFound;
return (perfectMatch || containsString);
}];

Core Data Predicate - Check if any element in array matches any element in another array

I'm trying to use a predicate to filter objects where an intersection exists between two arrays.
The NSManagedObject has an array(Of Strings) attribute named "transmissions". And there is another array(Of Strings) that will contain words to filter by, named "filters".
I'm not sure how to find if any elements in "transmissions" match any element in "filters".
I've tried
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"ANY SELF.transmission in[c] %#",transmissions];
or
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"ANY transmission in[c] %#",transmissions];
However, core data fetches no results where there should be some.
Try this.
NSPredicate *predicate = nil;
NSMutableArray *predicates = [NSMutableArray new];
for (NSString *transmission in transmissions) {
[predicates addObject:[NSPredicate predicateWithFormat:#"transmission == %#", transmission]];
}
if (predicates.count > 0)
predicate = [NSCompoundPredicate orPredicateWithSubpredicates:predicates];
// make fetch request using 'predicate'
You can use the keyword CONTAINS to check an object exists in a collection.
[NSPredicate predicateWithFormat:#"favouriteFilms CONTAINS %#", film]
I believe the same thing can be achieved with IN by switching the LHS and RHS of the predicate. I have only successfully implemented this functionality using CONTAINS however.

NSPredicate on an NSArray to search for any object

I have an array of objects with names, addresses, tel. nos, etc.
I want to be able to search the array for any occurrence of a term - whether in the name field, the address field, etc.
I have something like this in mind :
-(void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope {
// Update the filtered array based on the search text and scope.
// Remove all objects from the filtered search array
[self.searchResults removeAllObjects];
// Filter the array using NSPredicate
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF contains[c] %#",searchText];
searchResults = [NSMutableArray arrayWithArray:[contactArray filteredArrayUsingPredicate:predicate]];
}
This causes an exception "Can't use in/contains operator with collection".
UPDATE. I can now search on up to three fields. When I add a fourth (in any sequence), I get this exception: "Unable to parse the format string ..."
The Predicate code is now:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF.narrative contains[c] %# OR SELF.category contains[c] %# OR SELF.date contains[c] OR SELF.name contains[c] %#", searchText, searchText, searchText, searchText];
searchResults = [NSMutableArray arrayWithArray:[allDreams filteredArrayUsingPredicate:predicate]];
Is three a limit of predicate search fields? How do I get around this? Thanks again.
Just use a predicate string that checks for them all:
#"name contains[cd] %# OR address contains[cd] %#"
you can add as many as you want.
The only downside is that you'll need to add the same search string for each field you want to test, which can seem a bit ugly.
If your objects are dictionaries, then there is a way to truly search all values without necessarily knowing their names at compile time, using a subquery.
It works like this:
#"subquery(self.#allValues, $av, $av contains %#).#count > 0"
It uses the #allValues special key (or method call if you prefer) for dictionary objects and uses that to filter any value that contains your search string. If any is found (i.e., the count is positive), the object is included in the results.
Notice that this will examine all values indiscriminately, even those that you don't want to include if you have any in your dictionary.
I think your array item is not pure string, right?
Suppose your array item contains name attribute (even it's a dictionary), you can search it in this way:
NSPredicate * predicate =
[NSPredicate predicateWithFormat:#"name CONTAINS[cd] %# OR name LIKE[cd] %#", searchText, searchText];
here, name can be SELF.name.
HERE's an official document.

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