Strange NSPredicate Behaviour? - ios

Currently, I have a table view which is populated by video objects depending on the date they were recorded which was taken by using [nsdate date]; when they were recorded and saved. However, it is possible to record multiple videos in one day. I sort them by the date, and when there is more than one video with the same date, it displays the first one recorded on that date, with the most recent ones following in ascending order according to the most recent. I'm not sure why this happens but would like to check if there are more than one videos with the same date recorded, it organizes them by title. In SQL I could do something like:
ORDER BY DATE_RECORDED, TITLE ASC
Here is what I am currently doing:
AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
_managedObjectContext = [appDelegate managedObjectContext];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"whosVideo == %#", _currentPerson];
[request setPredicate:predicate];
NSEntityDescription *eval = [NSEntityDescription entityForName:#"Video" inManagedObjectContext:_managedObjectContext];
[request setEntity:eval];
NSSortDescriptor *sortDescriptor =
[[NSSortDescriptor alloc] initWithKey:#"date_recorded"
ascending:NO
selector:#selector(localizedCaseInsensitiveCompare:)];
NSArray *sortDescriptors = [[NSArray alloc]initWithObjects:sortDescriptor, nil];
[request setSortDescriptors:sortDescriptors];
NSError *error = nil;
NSMutableArray *mutableFetchResults = [[_managedObjectContext executeFetchRequest:request error:&error] mutableCopy];
if (mutableFetchResults == nil){
//handle error
}
[self setVideoArray:mutableFetchResults];
[self.tableView reloadData];
How would I handle adding a second predicate if there is more than one object recorded on the same date?

If you want to handle more than one predicate than you have to use NSCompoundPredicate try like this below:-
NSPredicate *predicate1 = [NSPredicate predicateWithFormat:#"whosVideo == %#", _currentPerson];
NSPredicate *predicate2 = [NSPredicate predicateWithFormat:#"whosVideo == %#", _someOther];
NSArray *subPredicates = [[NSArray alloc] initWithObjects:predicate1, predicate2,nil];
NSPredicate *predicate = [NSCompoundPredicate orPredicateWithSubpredicates:subPredicates];

Related

NSFetchedResultsController with complicate predicate

I'm developing the app with UICollectionView.
It's collection of events arranged by date. I'm using NSFetchedResultsController to fetch data from DB. But I need to fetch current and future events + 2 past events.
For NSFetchRequest I'm using NSCompoundPredicate
NSPredicate *nextPredicate = [NSPredicate predicateWithFormat: #"startAt >= %#", [NSDate date]];
NSPredicate *previousPredicate = [NSPredicate predicateWithFormat:#"startAt < %#", [NSDate date]];
NSCompoundPredicate *resultPredicate = [NSCompoundPredicate andPredicateWithSubpredicates: #[nextPredicate, previuosPredicate]];
fetchRequest.predicate = resultPredicate;
And I have no idea how to limit previousPredicate to receive only 2 items.
If you know the date of current event than create a method that return date of the last event to fetch and then set the predicate on the main NSFetchRequest to that date.
-(NSDate *)dateOfPastEvent{
NSFetchRequest *request = [[NSFetchRequest alloc]init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"EntityName" inManagedObjectContext:_mainContext];
[request setEntity:entity];
NSSortDescriptor *sortDesc = [NSSortDescriptor sortDescriptorWithKey:#"startAt" ascending:NO];
[request setSortDescriptors:#[sortDesc]];
NSError *error = nil;
NSArray *array = [_mainContext executeFetchRequest:request error:&error];
__block NSDate *pastEventDate;
[array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop){
if ([obj valueForKey:#"startAt"] == currentEventDate) {
if (array.count >= idx+2) {
pastEventDate = [[array objectAtIndex:idx+2] valueForKey:#"startAt"];
*stop = YES;
}
}
}];
return pastEventDate;
}
Now set the predicate for the above returned date
NSFetchRequest *requestForController = [[NSFetchRequest alloc]init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"EntityName" inManagedObjectContext:_mainContext];
[requestForController setEntity:entity];
NSPredicate *predicate = [NSPredicate predicateWithFormat: #"startAt >= %#", [self dateOfPastEvent]];

How do I query for values grouped by unique values in core data?

Consider the above model with to-many relation.
Let's assume I am querying to get managed-objects where patient first or last name is "xyz" (there are multiple records of "xyz").
How to get a unique "xyz" as dictionary object and repeated "xyz" Managed-objets as objects in that dictionary.
So far I am able to get distinct values and also I am able to get array of managed objects where "xyz" is the predicate string.
But I am unable to think of a way to get array of dictionaries with its objects as managed-objects of duplicate values.
I want to have an array of dictionaries with unique value as dictionary name and objets in dictionary should be managed-objects/dictionaries.
Could somebody please help me with this. I will provide more explanation if any part is not understood.
Thanks
#pragma mark - Search Patients By Patient Name
-(NSArray*)GetBillsForPatientwith:(NSString*)name{
NSMutableArray *AllBillsData = [[NSMutableArray alloc]init];
NSArray *_strings = [self GetSplitStrings:name];
NSManagedObjectContext *context = [CoreDataManager GetCoreDataManager].managedObjectContext;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:_Claims inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"patient_FirstName" ascending:YES];
NSArray *sortDescriptors = #[sortDescriptor];
[fetchRequest setSortDescriptors:sortDescriptors];
NSMutableArray *subPredicates = [NSMutableArray array];
for (NSString *strings in _strings) {
[subPredicates addObject:
[NSPredicate predicateWithFormat:#"patient_FirstName CONTAINS[cd] %# OR patient_LastName contains[cd] %#",strings,strings]];
}
NSCompoundPredicate *predicate_ = [[NSCompoundPredicate alloc]initWithType:NSOrPredicateType subpredicates:subPredicates];
[fetchRequest setReturnsObjectsAsFaults:NO];
[fetchRequest setPredicate:predicate_];
fetchRequest.returnsDistinctResults = YES;
fetchRequest.resultType = NSDictionaryResultType;
fetchRequest.propertiesToFetch = [NSArray arrayWithObjects:[[entity propertiesByName] objectForKey:#"patient_FirstName"],[[entity propertiesByName] objectForKey:#"patient_LastName"], nil];
//fetchRequest.propertiesToGroupBy = [NSArray arrayWithObjects:[[entity propertiesByName] objectForKey:#"patient_FirstName"],[[entity propertiesByName] objectForKey:#"patient_LastName"], nil];
NSError *error;
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
if (fetchedObjects == nil) {
// Handle the error.
}
for (NSDictionary *names in fetchedObjects) {
// [AllBillsData addObject:names];
[AllBillsData addObject:[self GetCorrespondingClaimsForNames:names]];
}
return AllBillsData;
}
//Entangled Method
-(NSArray*)GetCorrespondingClaimsForNames:(id)Names{
NSManagedObjectContext *context = [CoreDataManager
GetCoreDataManager].managedObjectContext;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:_Claims inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"patient_FirstName" ascending:YES];
NSArray *sortDescriptors = #[sortDescriptor];
[fetchRequest setSortDescriptors:sortDescriptors];
NSMutableArray *subPredicates = [NSMutableArray array];
[subPredicates addObject:
[NSPredicate predicateWithFormat:#"patient_FirstName ==[cd] %# AND patient_LastName ==[cd] %#",[Names valueForKey:#"patient_FirstName"],[Names valueForKey:#"patient_LastName"]]];
NSCompoundPredicate *predicate_ = [[NSCompoundPredicate alloc]initWithType:NSOrPredicateType subpredicates:subPredicates];
[fetchRequest setReturnsObjectsAsFaults:NO];
[fetchRequest setPredicate:predicate_];
NSError *error;
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
if (fetchedObjects == nil) {
// Handle the error.
}
return fetchedObjects;
}

NSPredicate returning all objects

I'm trying to use the NSPredicate to only get specific objects. The problem is that all the objects are fetched and it should only be specific objects where the condition is correct.
- (void)fetchDevices {
// Fetch the devices from persistent data store
NSManagedObjectContext *managedObjectContext = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"Songs"];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Songs" inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
// retrive the objects with a given value for a certain property
NSPredicate *predicate = [NSPredicate predicateWithFormat: #"selectedRow == %#" , selectedRowNumber];
devices = [[managedObjectContext executeFetchRequest:fetchRequest error:nil] mutableCopy];
[fetchRequest setPredicate:predicate];
}
The selectedRowNumber is an int declared in the .h file.
A look on my Songs entity:
PS. Please comment below if more information is required and i'll provide it.
You have to assign the predicate before executing the fetch request.
Also, if selectedRowNumber is an int then you have to use the %d format
instead of %#.
NSPredicate *predicate = [NSPredicate predicateWithFormat: #"selectedRow == %d" , selectedRowNumber];
[fetchRequest setPredicate:predicate];
devices = [[managedObjectContext executeFetchRequest:fetchRequest error:nil] mutableCopy];
Try this,
NSPredicate *predicate = [NSPredicate predicateWithFormat: #"selectedRow = %#" , [NSNumber numberWithInt: selectedRowNumber]];
[fetchRequest setPredicate:predicate];
devices = [[managedObjectContext executeFetchRequest:fetchRequest error:nil] mutableCopy];
there seems to be two problems.
I think you need to put [fetchRequest setPredicate:predicate]; before devices = [[managedObjectContext executeFetchRequest:fetchRequest error:nil] mutableCopy];
Also #"selectedRow == %d" seems not right. You may want to change it to #"selectedRow = %d"
And no need to set entity again.
So the code may look like this.
- (void)fetchDevices {
int selectedRowNumber = 1;
NSManagedObjectContext *managedObjectContext = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"Songs"];
NSPredicate *predicate = [NSPredicate predicateWithFormat: #"selectedRow = %d" , selectedRowNumber];
[fetchRequest setPredicate:predicate];
NSArray *devices = [[managedObjectContext executeFetchRequest:fetchRequest error:nil] mutableCopy];
}
Let me know how it goes :)
There is no need of setting Entity using setEntity.
[fetchRequest setPredicate:predicate]
Just set predicate, it will work.
- (void)fetchDevices {
// Fetch the devices from persistent data store
NSManagedObjectContext *managedObjectContext = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"Songs"];
// retrive the objects with a given value for a certain property
NSPredicate *predicate = [NSPredicate predicateWithFormat: #"selectedRow == %#" , selectedRowNumber];
[fetchRequest setPredicate:predicate];
devices = [[managedObjectContext executeFetchRequest:fetchRequest error:nil] mutableCopy];
[fetchRequest setPredicate:predicate];
}

Weird NSPredicate Behavior?

I have an NSPredicate which checks the relationship of an entity called "Eval(whosEval)" and if the relationship whosEval matches the full name of another entity, "Athlete(evals)", with the athlete that was selected, it displays all the Evals for that particular Athlete. Unfortunately, my predicate is doing weird things. If I have no athletes, and then add one, then add an eval, it shows all their evals, then I add another, the second athlete won't display any evals I added for that athlete and instead displays all of the other athlete's evals. Am I doing something incorrectly? All evals view controller is displayed prior to add Eval view controller.
allEvals.m
-(void)viewWillAppear:(BOOL)animated{
self.title = [NSString stringWithFormat:#"%#'s Evaluations",_athletesFullName];
AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
_managedObjectContext = [appDelegate managedObjectContext];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSFetchRequest *athleteRequest = [[NSFetchRequest alloc] init];
[athleteRequest setEntity:[NSEntityDescription entityForName:#"Athlete" inManagedObjectContext:_managedObjectContext]];
NSError *athleteError = nil;
NSPredicate *athletePredicate = [NSPredicate predicateWithFormat:#"full == %#", _athletesFullName];
[athleteRequest setPredicate:athletePredicate];
NSArray *results = [_managedObjectContext executeFetchRequest:athleteRequest error:&athleteError];
Athlete *currentAthlete = [results objectAtIndex:0];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"whosEval == %#", currentAthlete];
[request setPredicate:predicate];
NSEntityDescription *eval = [NSEntityDescription entityForName:#"Eval" inManagedObjectContext:_managedObjectContext];
[request setEntity:eval];
NSSortDescriptor *sortDescriptor =
[[NSSortDescriptor alloc] initWithKey:#"date_recorded"
ascending:NO
selector:#selector(localizedCaseInsensitiveCompare:)];
NSArray *sortDescriptors = [[NSArray alloc]initWithObjects:sortDescriptor, nil];
[request setSortDescriptors:sortDescriptors];
NSError *error = nil;
NSMutableArray *mutableFetchResults = [[_managedObjectContext executeFetchRequest:request error:&error] mutableCopy];
if (mutableFetchResults == nil){
//handle error
}
[self setEvalArray:mutableFetchResults];
[self.tableView reloadData];
}
addEval.m
-(void)saveEval{
//insert saving attributes here
AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
_managedObjectContext = [appDelegate managedObjectContext];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:[NSEntityDescription entityForName:#"Athlete" inManagedObjectContext:_managedObjectContext]];
NSError *error = nil;
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"full == %#", _whichAthletesEval];
[request setPredicate:predicate];
NSArray *results = [_managedObjectContext executeFetchRequest:request error:&error];
Athlete *currentAthlete = [results objectAtIndex:0];
[eval setWhosEval:currentAthlete];
NSLog(#"This Eval Belongs to: %#",eval.whosEval);
NSLog(#"whichAthletesEval value is equal to: %#", _whichAthletesEval);
}
NSError *error = nil;
if(![_managedObjectContext save:&error]){
//handle dat error
}
[self.navigationController popViewControllerAnimated:YES];
}
I have left inline comments (signed off with my initials) in your viewWillAppear: method that point out what I believe to be errors. Essentially, you fetch an arbitrary athlete instead of the one that's been selected (which I'm assuming is the source of _athletesFullName. As far as your saveEval method, your naming conventions are tough to follow so it's not clear to me what further issues you could have here.
-(void)viewWillAppear:(BOOL)animated{
self.title = [NSString stringWithFormat:#"%#'s Evaluations",_athletesFullName];
AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
_managedObjectContext = [appDelegate managedObjectContext];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSFetchRequest *athleteRequest = [[NSFetchRequest alloc] init];
[athleteRequest setEntity:[NSEntityDescription entityForName:#"Athlete" inManagedObjectContext:_managedObjectContext]];
NSError *athleteError = nil;
// No predicate is used at all for the contents of results here - see below - CV
NSArray *results = [_managedObjectContext executeFetchRequest:athleteRequest error:&athleteError];
NSPredicate *athletePredicate = [NSPredicate predicateWithFormat:#"full == %#", _athletesFullName];
// This predicate never used as another predicate is set a few lines below - CV
[request setPredicate:athletePredicate];
// currentAthlete will not necessarily be the one conforming to athletePredicate
// just the first in the array of every Athlete you've fetched - CV
Athlete *currentAthlete = [results objectAtIndex:0];
// because currentAthlete is arbitrary, you're now creating a predicate you likely didn't intend to - CV
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"whosEval == %#", currentAthlete];
[request setPredicate:predicate];
NSEntityDescription *eval = [NSEntityDescription entityForName:#"Eval" inManagedObjectContext:_managedObjectContext];
[request setEntity:eval];
NSSortDescriptor *sortDescriptor =
[[NSSortDescriptor alloc] initWithKey:#"date_recorded"
ascending:NO
selector:#selector(localizedCaseInsensitiveCompare:)];
NSArray *sortDescriptors = [[NSArray alloc]initWithObjects:sortDescriptor, nil];
[request setSortDescriptors:sortDescriptors];
NSError *error = nil;
NSMutableArray *mutableFetchResults = [[_managedObjectContext executeFetchRequest:request error:&error] mutableCopy];
if (mutableFetchResults == nil){
//handle error
}
[self setEvalArray:mutableFetchResults];
[self.tableView reloadData];
}

NSPredicate over different entities

I want to make NSPredicate over different entities. Here is my core database.
To make it simple, this is the query I should do.
Select c.c_name From Company c, Person p Where c.c_id = p.cu_company_id
Now I was wondering how my predicate should look like if I want to achieve the result like the query above.
Assuming that c_id and cu_company_id are integers, you can try
NSFetchRequest *fr = [[NSFetchRequest alloc] initWithEntityName:#"Person"];
NSError *error;
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"cu_company_id == data.company.c_id"];
[fr setPredicate:predicate];
NSArray *persons = [self.managedObjectContext executeFetchRequest:fr error:&error];
and once you have persons array, you can then loop through it and get the person's name. To get it the other way around
NSFetchRequest *fr = [[NSFetchRequest alloc] initWithEntityName:#"Company"];
NSError *error;
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"c_id == data.person.cu_company_id"];
[fr setPredicate:predicate];
NSArray *companies = [self.managedObjectContext executeFetchRequest:fr error:&error];
and once you have companies array, you can then loop through it and get the company's name.

Resources