fast research with nspredicate - iphone

In my application i have a large table of around 20.000 items. I am displaying it on tableview. But the search bar is too slow while doing dynamic search. I have read that NSPredicate method is high performance then NSRange.
I don't know how applicate this method.
My code is :
- (void)filterContentForSearchText:(NSString*)searchText
{
[self.filteredListContent removeAllObjects];
for (Book *book in listContent)
{
NSRange range = [book.name rangeOfString:searchText options:NSCaseInsensitiveSearch];
// is very very slow
if (range.location != NSNotFound)
{
[self.filteredListContent addObject:book];
}
}
}
Where i must insert NSPredicate, into our out the "for"?

- (void)filterContentForSearchText:(NSString*)searchText
{
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"name contains %#", searchText ];
self.filteredListContent = [NSMutableArray arrayWithArray:[listContent filteredArrayUsingPredicate:predicate]];
}

If filtering for instance NSArray, you can use
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"job == 'Programmer'"]
[listOfItems filterUsingPredicate:predicate];
if you want to make a fetch request use
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"title == %#", aTitle];
[request setEntity:[NSEntityDescription entityForName:#"DVD" inManagedObjectContext:moc]];
[request setPredicate:predicate];
NSError *error = nil;
NSArray *results = [moc executeFetchRequest:request error:&error];
// error handling code
[request release];
EDIT:
ssteinberg's example is simple and good, just one note - you can modify an operator using the key characters c and d within square braces to specify case and diacritic insensitivity respectively. Example [NSPredicate predicateWithFormat:#"name contains[cd] %#", searchString];

Related

NSPredicate - Evaluate with two conditions

I want to know how to evaluate NSString which satisfied two conditions using NSPredicate. For example how to check a string that should have atleast one upper case letter and atleast one number.
You can use ANDing for two or more condition.
NSManagedObjectContext *context = [appDelegate managedObjectContext];
NSEntityDescription *entityDescription = [NSEntityDescription entityForName:entityName inManagedObjectContext:context];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity:entityDescription];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"fbUID = %# AND birthDate = %d AND birthMonth = %d AND greetingYear = %d", uID, birthDate, birthMonth, greetingYear];
//NSLog(#"%# :",predicate);
[fetchRequest setPredicate:predicate];
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
You can check more on NSPredicate HERE.
One way to do this is:
NSCharacterSet *upperCaseCharacters = [NSCharacterSet characterSetWithCharactersInString:#"ABCDEFGHIJKLKMNOPQRSTUVWXYZ"];
NSCharacterSet *numbers = [NSCharacterSet characterSetWithCharactersInString:#"0123456789"];
NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
return ([evaluatedObject rangeOfCharacterFromSet:upperCaseCharacters].location != NSNotFound && [evaluatedObject rangeOfCharacterFromSet:numbers].location != NSNotFound);
}];
NSString *stringToEvaluate = #"aasad5D";
BOOL result = [predicate evaluateWithObject:stringToEvaluate];
Another solution is compound predicate, see this question for example how to use compound predicates create a Compound Predicate in coreData xcode iphone.
I found a solution for this by myself. I used regular expression with predicate to solve this issue.
NSPredicate *pwdCheck = [NSPredicate predicateWithFormat:#"SELF MATCHES %#", #"(?=.*?[A-Z])(?=.*?[0-9]).*"];
bool isValid = [pwdCheck evaluateWithObject:#"99a99A3w"];
Thanks for your help

how we can search all records using NSPredicate rather than setting fetchController nil?

I am showing all the records in UITableView easily by using:
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:#"Memo" inManagedObjectContext:[appDelegate managedObjectContext]];
[fetchRequest setEntity:entity];
NSSortDescriptor *sort = [[NSSortDescriptor alloc]
initWithKey:#"memodate" ascending:NO];
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:sort]];
NSFetchedResultsController *theFetchedResultsController =
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:[appDelegate managedObjectContext] sectionNameKeyPath:nil
cacheName:nil];
self.fetchedResultsController = theFetchedResultsController;
return _fetchedResultsController;
And for Searching records I am performing this:
- (void)searchBar:(UISearchBar *)aSearchBar textDidChange:(NSString *)searchText {
if (self.searchBar.text ==nil || [self.searchBar.text isEqualToString:#""])
{
self.fetchedResultsController=nil;
}
else
{
NSPredicate *predicate =[NSPredicate predicateWithFormat:#"memodesc CONTAINS[cd] %#", self.searchBar.text];
[_fetchedResultsController.fetchRequest setPredicate:predicate];
}
NSError *error = nil;
if (![[self fetchedResultsController] performFetch:&error]) {
// Handle error
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort(); // Fail
}
[self.tableView reloadData];}
My Question is what is the right way to search all records if searchText value is nil or ""
I am setting self.fetchedResultsController=nil; again for retrieving all the initial results.
But I don't think so this is the right way. It should be doable with predicate ?
Update:
Its working finw with this predicate
NSPredicate *predicate =[NSPredicate predicateWithFormat:#"1=1"];
[_fetchedResultsController.fetchRequest setPredicate:predicate];
But my app is crashing if I used this:
NSPredicate *predicate =[NSPredicate predicateWithFormat:#"All"];
[_fetchedResultsController.fetchRequest setPredicate:predicate];
Unable to parse the format string "ALL"'
And in swift:
let predicate = NSPredicate(value: true)
I know it's an old thread, but anyway...You can also use:
[NSPredicate predicateWithFormat:#"TRUEPREDICATE"];
Predicate format syntax in Apple's documentation
Just set the predicate to nil if you want to fetch all entities.

Database update using Core Data

I am trying to update my local database when app gets response from the web server. When app gets the update from web server, I fetch the data from the local database by matching the id with the response and get one row and perform update code but local database does not get updated and also does not give an error.
What should be the solution???
-(void)checkID:(NSMutableDictionary *)dict
{
NSDictionary *dictEvent = [dict objectForKey:#"Event"];
NSManagedObjectContext *context = [self managedObjectContext];
NSManagedObject *selectedManagedObject = nil;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc]init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Events" inManagedObjectContext:context];
NSSortDescriptor *sortDescObj = [[NSSortDescriptor alloc] initWithKey:#"event_id" ascending:YES];
NSError *error = nil;
NSPredicate *predicate = [NSPredicate predicateWithFormat:[NSString stringWithFormat:#"user_id=%# and event_id=%#",[NSNumber numberWithInt:[[dictEvent valueForKey:#"user_id"] intValue]],[NSNumber numberWithInt:[[dictEvent valueForKey:#"id"] intValue]]]];
NSLog(#"Predicate = %#",predicate);
NSArray *arrSortDescriptors = [NSArray arrayWithObject:sortDescObj];
[fetchRequest setSortDescriptors:arrSortDescriptors];
[fetchRequest setEntity:entity];
[fetchRequest setReturnsDistinctResults:YES];
[fetchRequest setPredicate:predicate];
NSArray *arrResult = [context executeFetchRequest:fetchRequest error:&error];
if ([arrResult count]>0)
{
NSArray *arrKey = [dictEvent allKeys];
NSArray *arrValue = [dictEvent allValues];
NSLog(#"ArrKey : %#\nArrValue : %#",arrKey,arrValue);
selectedManagedObject = [arrResult objectAtIndex:0];
for(int i = 0; i < [arrKey count] ; i++)
{
NSLog(#"selectedMng :- %#",selectedManagedObject);
NSLog(#"KEY: %#\t: %#",[arrKey objectAtIndex:i],[arrValue objectAtIndex:i]);
if ([[arrKey objectAtIndex:i]isEqualToString:#"id"])
{
[selectedManagedObject setValue:[arrValue objectAtIndex:i] forKey:#"event_id"];
}
else if([[arrKey objectAtIndex:i]isEqualToString:#"invited_status"])
{
[selectedManagedObject setValue:[arrValue objectAtIndex:i] forKey:#"invite_status"];
}
else
{
[selectedManagedObject setValue:[arrValue objectAtIndex:i] forKey:[arrKey objectAtIndex:i]];
}
}
if (! [selectedManagedObject.managedObjectContext save:&error])
{
NSLog(#"updateEntityIntoDataBaseNamed - Error :: %#", [error localizedDescription]);
}
// }
}
}
Besides modifying your predicate as suggested by #Martin
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"user_id=%# && event_id=%#",
[NSNumber numberWithInt:[[dictEvent valueForKey:#"user_id"] intValue]],
[NSNumber numberWithInt:[[dictEvent valueForKey:#"id"] intValue]]
];
note that in two cases, you are updating your object using non matching keys: this happens for id and event_id, and for invited_status and invite_status.
You cannot use stringWithFormat within predicateWithFormat. Your predicate should probably look like this:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"user_id=%# and event_id=%#",
[NSNumber numberWithInt:[[dictEvent valueForKey:#"user_id"] intValue]],
[NSNumber numberWithInt:[[dictEvent valueForKey:#"id"] intValue]]
];

Retrieve NSString from core-data error

The app needs to retrieve data from core-data then compare one of the values with an IF STATEMENT
MyAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [appDelegate managedObjectContext];
NSEntityDescription *getDetails = [NSEntityDescription entityForName:#"Detail" inManagedObjectContext:context];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:getDetails];
NSPredicate *pred = [NSPredicate predicateWithFormat:#"(genID = %i)", genID];
[request setPredicate:pred];
NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:#"genID" ascending:YES];
[request setSortDescriptors:[NSArray arrayWithObject:sort]];
NSError *error;
NSArray *objects = [context executeFetchRequest:request error:&error];
for (NSManagedObject *oneObject in objects) {
NSString *type = [oneObject valueForKey:#"type"];
if (type == #"straight") {
NSLog(#"straight");
//straight logic removed
}else if (type == #"Left") {
NSLog(#"Left");
//Left logic removed
}else {
NSLog(#"Else");
//Else logic removed
}
}
The issue is that it never goes into "straight" or "left" always the ELSE. Stepping through the code I can see that values do match straight and left, even dumped them into a log file shows them fine. What is wrong?
You need to compare strings using isEqual: or isEqualToString: or compare:.
For instance:
NSString *type = [oneObject valueForKey:#"type"];
if ([type isEqualToString:#"straight"])
// etc.
Search StackOverflow for "NSString equal" or "NSString compare" if you need more explanation. You aren't anywhere near the first person to ask this question, and you definitely won't be the last.

NSPredicate - Not working as expected

I have the following code in place:
NSString *mapIDx = #"98";
NSLog(#"map id: %#", mapIDx);
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"WayPoint" inManagedObjectContext:managedObjectContext];
[request setEntity:entity];
//NSPredicate *predicate = [NSPredicate predicateWithFormat:#"waypoint_map_id=%#", mapIDx];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"waypoint_map_id==%#", mapIDx];
[request setPredicate:predicate];
NSError *error;
listArray = [[managedObjectContext executeFetchRequest:request error:&error] mutableCopy];
[request release];
int arrayItemQuantity = [listArray count];
NSLog(#"Array Quantity: %d", arrayItemQuantity);
// Loop through the array and display the contents.
int i;
for (i = 0; i < arrayItemQuantity; i++)
{
NSLog (#"Element %i = %#", i, [listArray objectAtIndex: i]);
}
/*
NSInteger *xCoordinate = listArray[1];
NSInteger *yCoordinate = listArray[3];
NSLog(#"xCoordinate: %#", xCoordinate);
NSLog(#"yCoordinate: %#", yCoordinate);
CLLocationCoordinate2D coordinate = {xCoordinate, yCoordinate};
MapPin *pin = [[MapPin alloc]initwithCoordinates:coordinate];
[self.mapView addAnnotation:pin];
[pin release];
*/
[listArray release];
As you can see I'm trying to select specific objects from my database, anything with a waypoint_map_id of 98, but the NSPredicate is not working as I expected. Zero objects are getting selected.
Anyone any thoughts ?
The predicate with format does not covert the string "98" to a number. Instead it does
waypoint_map_id == "98"
... which is looking for string attribute. Change the predicate to:
NSInteger mapIdx=98;
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"waypoint_map_id==%d", mapIDx];
... which returns a predicate of:
waypoint_map_id == 98
Assuming that you definately have that object in your database, try adding quotes around the value?
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"waypoint_map_id==\"%#\"", mapIDx];
(clutching at straws!)
Your prediacte looks fine so I would instantly start to suspect the bug is somewhere else :
Is there definintely a waypoint with that id?
Is listArray nil i.e. something else has gone wrong with the request?
You don't check to see what the error is - perhaps that will give you more information?
NSError *error = nil;
NSArray *results = [managedObjectContext executeFetchRequest:request error:&error];
[request release];
if (nil == results || nil != error)
NSLog(#"Error getting results : %#", error);
listArray = [results mutableCopy];
Hope that's helpful at all!
Problem solved:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"waypoint_map_id contains[cd] %#", mapIDx];
[request setPredicate:predicate];