I'm setting the string of my predicate like this:
[NSString stringWithFormat:#"(name like '%#')",name]
but if name contains ' characters, for example if name is "family's" it crashes.
How can I fix this?
You don't need the ' in NSPredicate. They are being inserted automatically.
Just try
[NSPredicate predicateWithFormat:#"(name like %#)",name];
The reason this crashed was because the predicate value was interrupted.
If you create a string with format, like in your example, your going to end up with (name like 'family's'), which obviously can't work.
If you use predicateWithFormat: on the other hand, you can let it handle this itself. It will escape your special characters.
If you don't want/aren't able to use predicateWithFormat, then you can simply replace any apostrophes in your string.
I do this in my app, when checking my UISearchBar control, to see if the user's trying to look for particular CoreData records:
NSString* searchString = self.searchBar.text;
if (searchString.length != 0)
{
searchString = [searchString stringByReplacingOccurrencesOfString:#"'" withString:#"\\'"];
NSString* filter = [NSString stringWithFormat:#"companyName CONTAINS[cd] '%#'", searchString];
NSPredicate* predicate1 = [NSPredicate predicateWithFormat:filter];
// ...etc...
}
(This is a simplified version of a generic function I use, which actually searches CoreData using a variable number of filter strings, which is why I don't use predicateWithFormat directly.)
Related
I have a NSManagedObject with the following attributes:
status
kind
priority
Now I want to be able to filter my entity with these attributes respectively. So I would expect that I have to have a predicate along those lines:
status CONTAINS[c] ‘open’
I get really weird results, as soon as I have two variables in my predicate and I have to reverse the order of kind and value in my case so that I get the desired results:
NSString *kind = #"status"; // DEBUGGING
NSString *value = #"open"; // DEBUGGING
// This works although it defies all logic
NSString *predicate = [NSString stringWithFormat:#"('%#' CONTAINS[c] %#)", value, kind];
self.myFilterPredicate = [NSPredicate predicateWithFormat:predicate];
This however, does not work for some reason:
NSString *predicate = [NSString stringWithFormat:#"(%# CONTAINS[c] ‘%#‘)”, kind, value];
I cannot reproduce the exact problem, but generally you should not use stringWithFormat to create predicate. It causes problems as soon as the substituted key or value contain
any special characters like spaces or quotation marks.
A better way is
self.myFilterPredicate = [NSPredicate predicateWithFormat:#"%K == %#", kind, value];
%K is a placeholder to be replaced by a key path such as "status".
I have implemented a UISearchDisplayController that allows users to search a table. Currently the predicate I am using to search is as follows,
NSPredicate *resultPredicate = [NSPredicate predicateWithFormat:#"Name contains[cd] %#", searchText];
Now lets say a users searches for "beans, cooked" the corresponding matches are found in the table. But if the user enters the search text as "beans cooked" without the comma, there will be no matches found.
How can I re-write my predicate to "ignore" the commas when searching? In other words how can I re-write it so that it views "beans, cooked" being equal to "beans cooked" (NO COMMMA)?
First a disclaimer:
I think that what you are trying to do is to add some "fuzzyness" to your search algorithm, seen that you want to make your match insensitive to certain differences in user input.
Predicates (which are logic constructs) are by their very nature not fuzzy, so there is an underlying impedance mismatch between the problem and the tool chosen.
Anyway, one way to go about it could be to add a method to your model object class.
In this method, you can clean your name string so it only contains the most basic characters, say numbers, ascii letters and a space.
Being totally deterministic, such a method is effectively a read-only string property on your object, and as such it can be used to match in predicates.
Here is an implementation that removes punctuation, accents and diacritics:
- (NSString *)simplifiedName
{
// First convert the name string to a pure ASCII string
NSData *asciiData = [self.name dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
NSString *asciiString = [[[NSString alloc] initWithData:asciiData encoding:NSASCIIStringEncoding] lowercaseString];
// Define the characters that we will allow in our simplified name
NSString *searchCharacters = #"0123456789 abcdefghijklmnopqrstuvwxyz";
// Remove anything else
NSString *regExPattern = [NSString stringWithFormat:#"[^%#]", searchCharacters];
NSString *simplifiedName = [asciiString stringByReplacingOccurrencesOfString:regExPattern withString:#"" options:NSRegularExpressionSearch range:NSMakeRange(0, asciiString.length)];
return simplifiedName;
}
Now, a predicate could be made to search in the simplified name:
NSPredicate *pred = [NSPredicate predicateWithFormat:#"self.simplifiedName = %#", searchString];
You would of course want to clean the search string using the same algorithm used to clean the name, so it would probably be a good idea to factor it out into a general method to be used in both places.
Last, the simplifiedName method can also be added by implementing a category to the model object class so you don't have to modify its code, which is handy in case your object class is defined in an auto-generated file by Core Data.
This may be a bit hacky, but you could just remove the comma from the search term.
Example:
searchText = [searchText stringByReplacingOccurrencesOfString:#"," withString:#""];
NSPredicate *resultPredicate = [NSPredicate predicateWithFormat:#"Name contains[cd] %#", searchText];
The best solution I found for this type of problem is to actually add an entry in each items dictionary that has the same name but will all punctuations, commas, dashes, etc. removed like in this answer
All,
I am trying to use predicates to bring back a search return, giving precedence to strings that start with the search string VS. simply contained within it.
For example if the search string was "Objective-C", I want to get the filtered results back like this:
Objective-C a Primer
Objective-C Patterns
Objective-C Programming
All About Objective-C
How to program in Objective-C
Here is what I tried but since it's an OR, it clearly does not give precedence to the first condition. Is there a way to do a type of "chaining" with predicates? Thanks
NSPredicate *filter = [NSPredicate predicateWithFormat:#"subject BEGINSWITH [cd] %# OR subject CONTAINS [cd]", searchText,searchText];
NSArray *filtered = [myArray filteredArrayUsingPredicate: filter];
I don't think there's a way to do with NSPredicate by itself. What you seem to be asking for is sorted results. You do that by sorting the results after you get them back from the predicate. In this case, since the sort order isn't simply alphabetical, you should use NSArray's -sortedArrayUsingComparator: method. Something like this (not tested, typed off the top of my head).
NSArray *sortedStrings = [filtered sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
NSString *string1 = (NSString *)obj1;
NSString *string2 = (NSString *)obj2;
NSUInteger searchStringLocation1 = [string1 rangeOfString:searchString].location;
NSUInteger searchStringLocation2 = [string2 rangeOfString:searchString].location;
if (searchStringLocation1 < searchStringLocation2) return NSOrderedDescending;
if (searchStringLocation1 > searchStringLocation2) return NSOrderedAscending;
return NSOrderedSame;
}];
It looks like you're trying to sort your array using a predicate, or else trying to filter AND sort using just a predicate. Predicates return boolean values: either a string begins with or contains the search text, or it doesn't. Predicates don't tell you anything about relative order. Filtering an array removes those objects for which the predicate you supply returns NO, so only objects beginning with or containing the search text will be present in the filtered array. Indeed, since any string that begins with the search text also contains it, you could simplify your predicate to just the 'contains' part.
If you want to change the order of your filtered results, sort the filtered array with an appropriate comparator.
I want to know how searching of a string works in objective C when the iphone application has to support multiple language.
Assuming I have a search function that looks like this currently:
- (int)showSearchResultForQuery:(NSString *)query
{
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"name CONTAINS[cd] %# || address CONTAINS[cd] %#",query, query];
storesFiltered = [[NSMutableArray alloc]initWithArray:[stores filteredArrayUsingPredicate:predicate]];
int count = [storesFiltered count];
if(count > 0)
{
// we have some results
[resultTable reloadData];
}
return count;
}
This piece of code basically accepts a query string and update an array being used by a table using NSPredicate. I want to know, what do i need to take in consideration, if this function has to accepts multiple languages? chinese. english...japanese... will this function still work?
Thanks.
The only thing you need to consider is that the encoding of the strings in the array will be the same as the encoding of the query string.
For example, chinese is sometimes represented in UTF16, so you need to make sure that both the strings in the array and query string are encoded in UTF16.
Everything else will work out of the box.
I'm trying to use NSPredicate with 64-bit numbers, but for some reason it's failing. I create a predicate with the following string:
[NSString stringWithFormat:#"UID == %qu", theUID]
And it always fails. Yet if I loop through my objects and compare if ( UID == theUID ) I find one that is fine. I'm really confused as to why this isn't working correctly.
Does the above look correct? My predicates work fine for other integers or even strings. But this seems to be failing.
Thanks!
Edit: So strange... so I create my predicate by doing:
NSPredicate *myPredicate = [NSPredicate predicateWithFormat:myString];
Now when I print myString, and then print myPredicate (by doing NSLog(#"%#", blah);) I get:
String: UID == 17667815990388404861 Predicate: UID ==
9223372036854775807
It's the same string, why are these different?
Possibly theUID is not an unsigned value?
A more robust way to build predicates would be to pass in an NSNumber:
NSNumber *theUIDNum = [NSNumber numberWithUnsignedLongLong:theUID];
[NSString stringWithFormat:#"UID == %#", theUID]
This gives you the bonus ability to debug and print out the value of theUINum to make sure it got the transition right.
In your NSPredicate you can use something like this
let myNSNumber = NSNumber(value: theUID)
let predicate = NSPredicate(format: "UID = %i", myNSNumber.int64Value)