I have a Person Object which has two NSString properties; firstName and lastName.
I'm currently using an NSPredicate like so:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"(firstName contains[cd] %#) OR (lastName contains[cd] %#)", searchText, searchText];
So, for example, say I'm searching for the name "John Smith". In my search bar if I type "Joh", then John Smith will appear as an option. This is good and fine, but if I type in "John Sm" it will go blank.
How can I join firstName and lastName in the predicate so if I was searching for "John Sm" then John Smith would still appear as an option.
I hope this makes sense. Thanks.
EDIT:
To add a bit more clarification, I'm using the SearchDisplayController delegate method:
-(void)filterContentForSearchText:(NSString *)searchText scope:(NSString *)scope;
and I'm using the predicate like so:
newArray = [personObjectArray filteredArrayUsingPredicate:predicate];
Try this,
NSString *text = #"John Smi";
NSString *searchText = [text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
NSArray *array = [searchText componentsSeparatedByString:#" "];
NSString *firstName = searchText;
NSString *lastName = searchText;
NSPredicate *predicate = nil;
if ([array count] > 1) {
firstName = array[0];
lastName = array[1];
predicate = [NSPredicate predicateWithFormat:#"(firstName CONTAINS[cd] %# AND lastName CONTAINS[cd] %#) OR (firstName CONTAINS[cd] %# AND lastName CONTAINS[cd] %#)", firstName, lastName, lastName, firstName];
} else {
predicate = [NSPredicate predicateWithFormat:#"firstName CONTAINS[cd] %# OR lastName CONTAINS[cd] %#", firstName, lastName];
}
NSArray *filteredArray = [people filteredArrayUsingPredicate:predicate];
NSLog(#"%#", filteredArray);
Output:
(
{
firstName = John;
lastName = Smith;
}
)
Here text represents the searched text. The advantage with the above is, even if you pass text = #"Smi Joh"; or text = #"John "; or text = #" smi"; or text = #"joh smi ";, it will still show the above output.
The solution suggested above will not work with search strings that have more than two words. Here is a more thorough implementation in swift. This solution also allows for adding more fields on a record, if your goal is to implement a full text search across name, email, phone number, etc. In that case just update the NSPredicate to OR newField CONTAINS[cd] %# and be sure to add the extra $0 in the list of string replacements.
let searchText = search.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceCharacterSet())
let words = searchText.componentsSeparatedByCharactersInSet(NSCharacterSet.whitespaceCharacterSet())
let predicates = words.map { NSPredicate(format: "firstName CONTAINS[cd] %# OR lastName CONTAINS[cd] %#", $0,$0) }
let request = NSFetchRequest()
request.predicate = NSCompoundPredicate(type: NSCompoundPredicateType.AndPredicateType, subpredicates: predicates)
you can concatenate the fields into two common fields (firstLastName and lastFirstName )
- (NSString *)firstLastName {
return [NSString stringWithFormat:#"%# %#", self.firstName, self.lastName];
}
- (NSString *)lastFirstName {
return [NSString stringWithFormat:#"%# %#", self.lastName, self.firstName];
}
and then filter on this fields using 'contains[cd]'
[NSPredicate predicateWithFormat:#"(firstLastName contains[cd] %#) OR (lastFirstName contains[cd] %#)" , self.searchBar.text, self.searchBar.text];
Related
I'm using to get some of the content of nsmutable array and it work fine if I don't use nsstring to make the query:
NSLog(#"user information %#", [usersInfo filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"%K == 'Joe", #"id"]]);
But try to use a nsstring to query for the user it doesn't work:
NSString *user="Joe";
NSLog(#"user information %#", [usersInfo filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"%K == user", #"id"]]]);
any of you knows what I'm doing wrong? or what would be the best of doing it using NSString to query for users?
When you write
NSString *user = #"Joe";
... [NSPredicate predicateWithFormat:#"%K == user", #"id"]
you seem to expect that "user" in the predicate is replaced by the contents ("Joe") of the NSString variable, but this is not correct.
You have to give the string
as another argument to the predicate and add the %# format that will be expanded by the string.
NSString *user = #"Joe";
... [NSPredicate predicateWithFormat:#"%K == %#", #"id", user]
Here %K (which is var arg substitution for a key path) will be
substituted by the key "id", and %# (which is
var arg substitution for an object value) will be substituted
by the contents of the user variable.
Using %K expansion instead of
[NSPredicate predicateWithFormat:#"id == %#", user]
has the advantage that it works correctly even if the key is a
reserved word in the predicate format string syntax.
I am unable to gues why you are using "%K == 'Joe"
Anyways, You can use predicate as:
[NSPredicate predicateWithFormat:#"object.property LIKE[c] %#", stringValue];
Or,
[NSPredicate predicateWithFormat:#"object.property==[c] %#", stringValue];
try this:
NSString *user="Joe";
NSLog(#"user information %#", [usersInfo filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"SELF contains [cd] %#", user]]]);
Make your predicate format string before don't try to add property name inside predicate format :
NSString *predicateFormat = [NSString stringWithFormat:#"%# == %%#",#"id"];
NSPredicate *predicate = [NSPredicate predicateWithFormat: predicateFormat, #"Joe"];
NSArray *filteredArray = [mutableArray filteredArrayUsingPredicate:predicate];
Use:
NSLog(#"user information %#", [usersInfo filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:[NSString stringWithFormat:#"%K == '%#'",#"id",user]]]);
When you use:
NSLog(#"user information %#", [usersInfo filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"%K == user", #"id"]]]);"
The user will be a part of the string, it won't replace with the content of the NSString object.
How can I use NSPredicate to filter the values from array ivar against search query ? I manage to use NSPredicate against string ivar. I have one user defined class named as "User" which is used to create User from AddressBook. Here is my code
// User Defined Class named as "User"
// In User.h
#interface User : NSObject {
NSString *firstName;
NSString *lastName;
NSString *company;
UIImage *userImage;
NSArray *phoneNumberArray;
NSArray *emailArray;
NSArray *urlArray;
NSArray *addressArray;
NSString *notes;
NSString *dateOfBirth;
}
// In SearchViewController.m
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText{
// contactsArray is NSMutableArray
contactsArray = [Search searchContactsWithQuery:searchText];
NSLog(#"contactsArray count => %d",[contactsArray count]);
[contactsTableView reloadData];
}
// In Search.m
+(NSMutableArray*)searchContactsWithQuery:(NSString*)query {
NSLog(#"Query => %#",query);
NSMutableArray* predicates=[[NSMutableArray alloc] init];
// Create all predicates
NSPredicate * firstNamePredicate = [NSPredicate predicateWithFormat:#"firstName contains %#",query];
[predicates addObject:firstNamePredicate];
NSPredicate * lastNamePredicate = [NSPredicate predicateWithFormat:#"lastName contains %#",query];
[predicates addObject:lastNamePredicate];
NSPredicate * companyPredicate = [NSPredicate predicateWithFormat:#"company contains %#",query];
[predicates addObject:companyPredicate];
// Don't know how to use on array
// === START of ARRAY Predicate ====
NSPredicate *phoneNoPredicate = [NSPredicate predicateWithFormat:#"phoneNumberArray IN %#",query];
[predicates addObject:phoneNoPredicate];
NSPredicate *emailPredicate = [NSPredicate predicateWithFormat:#"emailArray contains %#",query];
[predicates addObject:emailPredicate];
NSPredicate *urlPredicate = [NSPredicate predicateWithFormat:#"urlArray contains %#",query];
[predicates addObject:urlPredicate];
NSPredicate *addressPredicate = [NSPredicate predicateWithFormat:#"addressArray contains %#",query];
// === END of ARRAY Predicate ====
NSPredicate *notesPredicate = [NSPredicate predicateWithFormat:#"notes contains %#",query];
[predicates addObject:notesPredicate];
NSPredicate *dobPredicate = [NSPredicate predicateWithFormat:#"dateOfBirth contains %#",query];
[predicates addObject:dobPredicate];
// Add predicates to array
NSPredicate *compoundPredicate = [NSCompoundPredicate orPredicateWithSubpredicates:predicates];
NSArray * filteredArray = [APP_DELEGATE.allUsersArray filteredArrayUsingPredicate:compoundPredicate];
return [NSMutableArray arrayWithArray:filteredArray];
}
phoneNumberArray,emailArray,urlArray & addressArray are used because user may have multiple entries for phone no , email, address , url like HOME , WORK, iPhone, Other etc.
How can I use predicates on array? Any kind of help is appreciated. Thanks
You are doing the correct way, but you need to create your Predicates with String values instead of passing the Objects directly.
You need to use %k instead of %# like
[NSPredicate predicateWithFormat:#"firstName contains %k",query];
Please let me know if this helps.
I have a simple Core Data model with two string attributes (size and category). Given a search string like 'small widget' is it possible to return records that match all the query words to at least one attribute (i.e. all records with 'small' size and 'widget' category)? I currently have:
NSString *search = #"small widget"
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"size contains[cd] %# OR category contains[cd] %#", search, search];
...
This won't return any results (as no category or size equals "small widget"). Any suggestions? Note that the search strings will be user entered from a text field and may come in any order so I can't split up manually.
I haven't tested it but it looks like your want this:
NSString *search = #"small widget";
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"%# contains[cd] size AND %# contains[cd] category", search, search];
The search string would contain (or not) the size and the category, you should ask if the current size or category is contained on the search string.
You could also split the search string and modify your predicate. You should them to identify the one that performs better
NSString *search = #"small widget";
NSArray *array = [search componentsSeparatedByString:#" "];
NSMutableArray *subPredicates = [NSMutableArray array];
for (NSString *q in array) {
[subPredicates addObject:
[NSPredicate predicateWithFormat:#"size contains[cd] %# OR category contains[cd] %#", q, q]];
}
NSCompoundPredicate *predicate = [[[NSCompoundPredicate alloc] initWithType:NSAndPredicateType
subpredicates:subPredicates] autorelease];
For Swift you can fetch multiple columns by using propertiesToFetch
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "<YOUR ENTITY NAME>")
let predicate = NSPredicate(format: "<YOUR PREDICATE>")
fetchRequest.predicate = predicate
fetchRequest.resultType = .dictionaryResultType
// Here is where you can query multiple columns, you can add as much columns as you want and fetch them as an array of dictionaries
fetchRequest.propertiesToFetch = ["column1", "column2", "column3, "column4"]
let sort = NSSortDescriptor(key: "column1", ascending: false)
fetchRequest.sortDescriptors = [sort]
I'm building a NSPredicate using the code below for an iPhone app. The logging shows the prediate to be: location CONTAINS "head" AND shape CONTAINS "oval" AND texture CONTAINS "bumpy" AND colour CONTAINS "red"
I get no results. If I limit the predicate to a single item it will work, more than 1 fails.
Can anyone tell me why?
Many thanks
NSMutableArray *subPredicates = [[NSMutableArray alloc] init];
for (Ditem in self.tableDataSource) {
NSString *Title = [Ditem valueForKey:#"Title"];
NSString *Value = [Ditem valueForKey:#"Value"];
if([[Value lowercaseString] isEqualToString: #"all"]){
Value = #"";
}
else{
NSPredicate *p = [NSComparisonPredicate predicateWithLeftExpression:[NSExpression expressionForKeyPath:[Title lowercaseString]] rightExpression:[NSExpression expressionForConstantValue:[Value lowercaseString]] modifier:NSDirectPredicateModifier type:NSContainsPredicateOperatorType options:0];
[subPredicates addObject:p];
}
}
NSPredicate *predicate = [NSCompoundPredicate andPredicateWithSubpredicates:subPredicates];
NSLog(#"predicate: %#", predicate);[self.fetchedResultsController.fetchRequest setPredicate:predicate];
Your predicate is requiring that all of the values in your filterable objects be strings. Is that correct?
Also, I would simplify your subpredicate creation to:
NSPredicate * p = [NSPredicate predicateWithFormat:#"%K CONTAINS %#", [Title lowercaseString], [Value lowercaseString]];
Building a search with some custom objects and three scopes: All, Active, and Former. Got it working with the below code:
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString *)scope {
[[self filteredArtists] removeAllObjects];
for (HPArtist *artist in [self artistList]) {
if ([scope isEqualToString:#"All"] || [[artist status] isEqualToString:scope]) {
NSComparisonResult result = [[artist displayName] compare:searchText options:(NSCaseInsensitiveSearch|NSDiacriticInsensitiveSearch) range:NSMakeRange(0, [searchText length])];
if (result == NSOrderedSame) {
[[self filteredArtists] addObject:artist];
}
}
}
}
This works fine and takes scope into account. Since I wanted to search four fields at at time, this question helped me come up with the below code:
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString *)scope {
[[self filteredArtists] removeAllObjects];
NSPredicate *resultPredicate = [NSPredicate predicateWithFormat:#"familyName CONTAINS[cd] %# OR familyKanji CONTAINS[cd] %# OR givenName CONTAINS[cd] %# OR givenKanji CONTAINS[cd] %#", searchText, searchText, searchText, searchText];
[[self filteredArtists] addObjectsFromArray:[[self artistList] filteredArrayUsingPredicate:resultPredicate]];
}
However it no longer takes scope into account.
I have been playing around with if statements, adding AND scope == 'Active', etc. to the end of the statement and using NSCompoundPredicates to no avail. Whenever I activate a scope, I'm not getting any matches for it.
Just a note that I've seen approaches like this one that take scope into account, however they only search inside one property.
Here's about how I would do it:
NSPredicate * template = [NSPredicate predicateWithFormat:#"familyName CONTAINS[cd] $SEARCH "
#"OR familyKanji CONTAINS[cd] $SEARCH "
#"OR givenName CONTAINS[cd] $SEARCH "
#"OR givenKanji CONTAINS[cd] $SEARCH"];
- (void)filterContentForSearchText:(NSString *)search scope:(NSString *)scope {
NSPredicate * actual = [template predicateWithSubstitutionVariables:[NSDictionary dictionaryWithObject:search forKey:#"SEARCH"]];
if ([scope isEqual:#"All"] == NO) {
NSPredicate * scopePredicate = [NSPredicate predicateWithFormat:#"scope == %#", scope];
actual = [NSCompoundPredicate andPredicateWithSubpredicates:[NSArray arrayWithObjects:actual, scopePredicate, nil]];
}
[[self filteredArtists] setArray:[[self artistList] filteredArrayUsingPredicate:actual]];
}