NSPredicate against NSArray in iphone - iphone

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.

Related

Filter array in ios checking multiple properties

I have an array of custom objects. The custom object look like this
#interface User : NSObject
#property(nonatomic, strong)NSString *user_Id;
#property(nonatomic, strong)NSString *user_Name;
#property(nonatomic, strong)NSString *user_UserName;
#end
I have to filter the array checking 2 properties.That is if I search a then it should get list of users filtered from array contains a in user_Name or user_Id .How can i achieve this? For a single property i know[user_Name]
NSString *predicateString = #"user_Name MATCHES[c] %#";
NSString *matchString = [NSString stringWithFormat: #".*%#.*",searchText];
NSPredicate *predicate =[NSPredicate predicateWithFormat:predicateString, matchString];
self.searchResults = [userArray filteredArrayUsingPredicate:predicate];
You can join predicate conditions with OR, such as:
NSString *predicateString = #"(user_Name MATCHES[c] %#) OR (user_Id MATCHES[c] %#)";
Alternately, you could filter the array by using indexesOfObjectsPassingTest: with an appropriate test block and then objectsAtIndexes: to get an array of the objects passing the test.
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"(user_Name== %#) || (user_Id== %#), <name>, <id>];
Try to use this predicate string
NSString *predicateString = #"user_Name MATCHES[c] %# OR user_Id MATCHES[c] %#";

About UISearchBar:textDidChange of predicateWithFormat

I have a question,I don't know why Method 2 can't get value ?
Method 1:can get value!
-(void) searchBar:(UISearchBar *) searchBar textDidChange:(NSString *)searchText{
NSString *firstName = #"55566666666";
AllMainEventRows3 = [AllMainEventRows mutableCopy];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF contains[cd] %#",firstName];
NSArray *filteredArray = [AllMainEventRows3 filteredArrayUsingPredicate:predicate];
NSLog(#"filteredArray: %#", filteredArray);
}
LOG:
filteredArray: (
{
JoinSuccess = 55566666666;
bmabout = "";
bmaddr = "\U53f0\U5317\U5e02\U9752\U5cf6\U6771\U8def99\U865f10F-6";
})
Method 2:can't get value!
-(void) searchBar:(UISearchBar *) searchBar textDidChange:(NSString *)searchText{
AllMainEventRows3 = [AllMainEventRows mutableCopy];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF contains[cd] %#",searchText];
NSArray *filteredArray = [AllMainEventRows3 filteredArrayUsingPredicate:predicate];
NSLog(#"filteredArray: %#", filteredArray);
}
LOG:
filteredArray:(
)
textDidChange Tells the delegate that the user changed the search text is delegate method of UISearchBarDelegate and it is called whenever text is changed within the search bar, I don't think the second method would get value because delegate methods are fired automatically means you are not calling them and you can not create multiple delegate methods with same name. you could declare some custom method for that purpose.
Problems in Predicate? would refer to usage of NSPredicate

NSPredicate of NSArray in NSDictionary

My Array
(
{id:1,data:(#"macbook",#"mac mini")},
{id:2,data:(#"ipad",#"ipod")},
{id:3,data:(#"macbook",#"ipod")}
)
I have a predicate
NSString *text = #"mac";
[NSPredicate predicateWithFormat:#"(data contains[cd] %#)",text];
[array filteredArrayUsingPredicate:predicate];
but it doesn't loop over my array inside my dictionary
(my result should be an array containing 2 objects with id 1 and 3)
NSString* text = #"mac" ;
NSPredicate* predicate = [NSPredicate predicateWithFormat:#"any data contains[cd] %#",text] ;
NSArray* filteredArray = [theArray filteredArrayUsingPredicate:predicate] ;
I personally find NSPredicate formats very error prone.
You may consider using a block in order to filter your NSArray
NSString * filterString = #"mac";
NSIndexSet * indexes = [array indexesOfObjectsPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {
NSArray * entry = obj[#"data"];
for (NSString * value in entry)
if ([value rangeOfString:filterString].location != NSNotFound)
return YES;
return NO;
}];
NSArray * filteredArray = [array objectsAtIndexes:indexes];
It's definitely longer and more verbose, but I find it definitely easier to read and debug.

object and NSPredicate

In my application i have a large table of around 12000 entries. I am displaying it on tableview. But the search bar is too slow while doing dynamic search. I have read that NSPredicate method is more permorfant then NSRange.
This is my old the code:
[self.filteredListContent removeAllObjects];
listContent = [[NSArray alloc] initWithArray:[dbAccess getAllBooks]];
for (Book *book in listContent)
{
NSRange range = [book.textBook rangeOfString:searchText options:NSCaseInsensitiveSearch];
if (range.location != NSNotFound)
{
[self.filteredListContent addObject:book];
}
}
My new code:
[self.filteredListContent removeAllObjects];
listContent = [[NSArray alloc] initWithArray:[dbAccess getAllBooks]];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF like[c] %#",searchText];
[self.filteredListContent addObject:[listContent filteredArrayUsingPredicate:predicate]];
When i try to execute this code i received this error: "Can't do regex matching on object .'"
I would do something more like...
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"%k like[c] %#",propertyIAmLookingFor,searchText];
Is your book class a string? If not then you cant use SELF like. You need to substitute the name of the property you are comparing.

NSCompoundPredicate fails to match

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