find specific objects in managedObjectContext and calculate sum - ios

I am trying to wrap my head around CoreData. So I am having a static tableview for statistics with labels like: "number of entries", "total distance", "total time" etc. I have my coredata entity called "Recording" and its attributes "startDate","duration",... and I would like to fetch all entries from the CURRENT week and then on one hand display the number of entries and on the other hand the total time. I know how to set up a fetch request (thank you, google)
- (NSFetchedResultsController *)fetchedResultsController
{
if (_fetchedResultsController != nil)
{
return _fetchedResultsController;
}
if (self.managedObjectContext)
{
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Recording" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"startDate" ascending:NO];
NSArray *sortDescriptors = [NSArray arrayWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
NSFetchedResultsController *frc = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:nil
cacheName:nil];
frc.delegate = self;
self.fetchedResultsController = frc;
NSError *error = nil;
if (![self.fetchedResultsController performFetch:&error])
{
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
}
return _fetchedResultsController;
}
but I am stuck with this special idea and I am not even sure if I need to do it with this...

I solved it now using:
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc]init];
NSEntityDescription *theType = [NSEntityDescription entityForName:#"Recording" inManagedObjectContext:self.managedObjectContext];
fetchRequest.entity = theType;
NSDate *firstDayOfCurrentWeek = [self firstWeekday:[NSDate date]];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"((startDate > %#) AND (startDate <= %#))",firstDayOfCurrentWeek, [NSDate date]];
fetchRequest.predicate = predicate;
NSSortDescriptor *theDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"startDate" ascending:NO];
NSArray *sortDescriptors = [NSArray arrayWithObjects:theDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
and finding the sum/total like this:
NSFetchedResultsController *fetchedResults = self.fetchedResultsController;
NSInteger allWorkouts = [[fetchedResults fetchedObjects] count];
CGFloat totalDistance = [[[fetchedResults fetchedObjects] valueForKeyPath: #"#sum.distance"] floatValue];
Thanks for the hint with the predicate :)

NSPredicate is what you're searching for.
https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CoreData/Articles/cdFetching.html

Related

NSPredicate that can find an object and search attributes of the that object

The following finds all Master objects with Master.nameLast equally a passed in string. Though I want the NSPredicate to check if the object matches Master.nameLast and if this object has Master.batting.ab > 0
- (NSFetchedResultsController *)fetchedResultsController {
if(_fetchedResultsController != nil) {
return _fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSManagedObjectContext *context = [self managedObjectContext];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Master" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"debut" ascending:YES];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"nameLast = [c]%#", nameLast];
[fetchRequest setPredicate:predicate];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
fetchRequest.sortDescriptors = sortDescriptors;
_fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:context sectionNameKeyPath:nil cacheName:nil];
return _fetchedResultsController;
}
I want something that finds all Master objects matching the passed in string and have Master.batting.ab > 0
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"(nameLast = [c]%#) AND (ANY batting.ab > 0)", nameLast];

Core Data - Fetch last objects of an array of objects?

Using below code, I am fetching an array of emails from Core Data. I pass an array of emailId as the predicate to the fetch and it returns all emails for each of the emailIds.
However, I only need last object (or last email) of each of the emailIds in the predicate. Currently, I take the output of this fetch and use separate sorting to filter the objects I want. However, I would like the filtering in this fetch itself. Is there any way to do that?
- (NSFetchedResultsController *)getLatestEmail:(NSArray *)emailId
{
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"email_id IN %#", emailId];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Emails"
inManagedObjectContext:self.context];
[fetchRequest setEntity:entity];
[fetchRequest setPredicate:predicate];
[fetchRequest setFetchBatchSize:20];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"last_update_timestamp" ascending:NO];
NSArray *sortDescriptors = #[sortDescriptor];
[fetchRequest setSortDescriptors:sortDescriptors];
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc]
initWithFetchRequest:fetchRequest
managedObjectContext:self.context
sectionNameKeyPath:nil
cacheName:#"Master"];
aFetchedResultsController.delegate = self;
self.emailFetchedResultsController = aFetchedResultsController;
NSError *error = nil;
if (![self.emailFetchedResultsController performFetch:&error])
{
NSLog(#"getLatestEmail failed %#, %#", error, [error userInfo]);
}
NSArray *emails = [[self.context executeFetchRequest:fetchRequest error:&error] mutableCopy];
return _emailFetchedResultsController;
}
I tried adding .lastobject to [[self.context executeFetchRequest:fetchRequest error:&error] mutableCopy], but didn't work. I couldn't also find any documentation that suggests that this is possible. However, I am sure, someone would have tried it before and would like to hear their opinion.
Invert your sort descriptor, then just set the batch size to 1 and the offset to zero.

NSFetchedResultsController with many NSSortDescriptor

in my app I am sorting an entity using only one NSSortDescriptor, but now I need to filter the data for 4 different attributes. This is the code for the current sorting, the result are all records from the entity sorted by the attribute 'displayOrder', but I need to filter the records by four other attributes(year,month,day,group), all of them integer32 except group which is string :
- (NSFetchedResultsController *)fetchedResultsController
{
if (fetchedResultsController) return fetchedResultsController;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity =
[NSEntityDescription entityForName:#"FavoriteThing"
inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
NSSortDescriptor *sortDescriptor =
[[NSSortDescriptor alloc] initWithKey:#"displayOrder"
ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc]
initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
NSFetchedResultsController *aFetchedResultsController =
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:managedObjectContext
sectionNameKeyPath:nil cacheName:#"ThingsCache"];
aFetchedResultsController.delegate = self;
[self setFetchedResultsController:aFetchedResultsController];
[aFetchedResultsController release];
[fetchRequest release];
[sortDescriptor release];
[sortDescriptors release];
return fetchedResultsController;
}
I have tried adding a predicate:
NSNumber *yearSearched = 1965;
NSPredicate *yearPredicate = [NSPredicate predicateWithFormat:#"todoYear = %#", yearSearched];
[fetchRequest setPredicate:yearPredicate];
But after running the app there is an error EXC_BAD_ACCESS at the line NSPredicate *yearPredicate = [NSPredicate predicateWithFormat:#"todoYear = %#", yearSearched];
Where the attribute 'todoYear' is from type Integer32
Your NSNumber creation is wrong (you're just setting the pointer to an integer). It should be:
NSNumber *yearSearched = #1965;
NSPredicate *yearPredicate = [NSPredicate predicateWithFormat:#"todoYear = %#", yearSearched];
[fetchRequest setPredicate:yearPredicate];

NSDecimalNumber sum

In my ios project I use two entities (CoreData): Person and Gifts with To-Many Relationship
I know how to calculate the sum of gifts to one person:
NSDecimalNumber *orderSum=[person.gifts valueForKeyPath:#"#sum.priceOfGift"];
But how do I calculate the sum of all persons?
-(NSFetchedResultsController *) fetchedResultsController {
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
//NSError *error = nil;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Person"
inManagedObjectContext:[self managedObjectContext]];
[fetchRequest setEntity:entity];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"nameOfPerson"
ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
_fetchedResultsController = [[NSFetchedResultsController alloc]initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:#"nameOfGroup" cacheName:nil];
_fetchedResultsController.delegate = self;
return _fetchedResultsController;
}
The "sum of all persons" would be to fetch all persons and count them
int sum = fetchedObjects.count;
But perhaps you mean the "sum of the prices of all gifts of all persons". If you think about it, it is the same as the sum of the prices of all gifts. Thus, you can just fetch the gifts and calculate
NSNumber *sum = [self.fetchedResultsController.fetchedObjects
valueForKeyPath:#"#sum.priceOfGift"];
You can try to fetch all the Persons, then use the sum
NSError *error = nil;
NSFetchRequest *request = [[NSFetchRequest alloc] init];
request.entity = [NSEntityDescription entityForName:#"Person" inManagedObjectContext:managedObjectContext];
request.predicate = nil;
request.sortDescriptors = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:#"index" ascending:YES]];//some parameter
NSArray *persons = [managedObjectContext executeFetchRequest:request error:&error];
NSDecimalNumber *orderSum;
for (Person *person in persons)
orderSum = [NSDecimalNumber decimalNumberWithDecimal:orderSum.decimalValue
+[person valueForKeyPath:#"gifts.#sum.priceOfGift"].decimalValue]
Though, now work with NSDecimalNumber look a bit uncomfortable.

NSFetchRequest, possible to filter by aggregate functions?

I am trying to pull all the Actions which belong to the latest Session.
In SQL this would be a simple JOIN ... WHERE Session.startTime = MAX(Session.startTime), but with Core Data I can't figure out how to do this without resorting to two steps as below. Any ideas?
// Step 1. Get the latest session
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:[NSEntityDescription entityForName:#"Session" inManagedObjectContext:self.managedObjectContext]];
[request setSortDescriptors:[NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:#"startTime" ascending:NO]]];
[request setFetchLimit:1];
Session *lastSession = nil;
NSArray *objects = [self.managedObjectContext executeFetchRequest:request error:NULL];
if ([objects count] > 0)
{
NSLog(#"max: %#", [objects objectAtIndex:0]);
lastSession = [objects objectAtIndex:0];
}
// Step 2. Get that session's actions
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Action" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
NSSortDescriptor *sectionSort = [[NSSortDescriptor alloc] initWithKey:#"session.startTime" ascending:NO selector:nil];
NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:#"startTime" ascending:NO selector:nil];
[fetchRequest setSortDescriptors:[NSArray arrayWithObjects:sectionSort, sort, nil]];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"session.startTime = %#", lastSession.startTime];
//**Can't I do something like this instead?**
//NSPredicate *predicate = [NSPredicate predicateWithFormat:#"session.startTime = session.#max.startTime"];
[fetchRequest setPredicate:predicate];
[fetchRequest setFetchBatchSize:10];
[self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:#"session.startTime" cacheName:#"ActionHistory"];
self.fetchedResultsController.delegate = self;
NSError *error;
if (![self.fetchedResultsController performFetch:&error])
{
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort(); // Fail
}
Edit: I do need to stick with using NSFetchedResultsController due reasons not fully detailed above.
I would advise you to make a relationship beetwen Session and Action.
Then, after you fetch the session you need, you could get all that session's actions as easy ss lastSession.actions.
Edit
Maybe you can do
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"session = %#",lastSession];

Resources