NSExpression and Core Data - objective-c

I am at beginner level for Core Data. I am creating an app which can calculate expenses bared by each member of the house. I have an Entity named "Expenses" and properties are
payer
date
amt (amount)
desc (description)
How do I create an array using NSExpression or any other way which can return Total of expense amount for each payer.
P.S. = I can get the 'sum' of 'amt' for specific string by following code
NSExpression *keyPathExpression = [NSExpression expressionForKeyPath:#"amt"];
NSExpression *expression = [NSExpression expressionForFunction:#"sum:"arguments:[NSArray arrayWithObject:keyPathExpression]];
NSExpressionDescription *expressionDescription = [[NSExpressionDescription alloc] init];
NSPredicate *predicate = [[NSPredicate alloc]init];
NSString *mem = #"Chintu";
predicate = [NSPredicate predicateWithFormat:
#"payer like %#", mem];
[expressionDescription setName:#"sumExpenses"];
[expressionDescription setExpression:expression];
[expressionDescription setExpressionResultType:NSDecimalAttributeType];
[request setPropertiesToFetch:[NSArray arrayWithObject:expressionDescription]];
[request setPredicate:predicate];

Related

CoreData fetch request - complex predicate

How would I simplify this using (ideally) a single predicate with key-value coding collection operators? (The array of interest in the end is filteredGames.)
NSManagedObjectContext *context;
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:[NSEntityDescription entityForName:#"Game" inManagedObjectContext:context];
NSArray *games = [context executeFetchRequest:request error:nil];
NSMutableArray *filteredGames = [[NSMutableArray alloc] init];
for (Game *game in games) {
NSInteger maxRoundNumber = [game.rounds valueForKeyPath:#"#max.number"];
Round *maxRound = [[game.rounds filteredSetUsingPredicate:[NSPredicate predicateWithFormat:#"number = %d", maxRoundNumber]] anyObject];
if (maxRound.someBoolProperty)
[filteredGames addObject:game];
}
Game has an NSSet property rounds of Rounds, and Round has an NSInteger property number, and I'm looking for those Games whose highest-number Round has some BOOL property someBoolProperty.
This seems to do what you want:
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"Game"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"ANY SUBQUERY(rounds, $r, $r.someBoolProperty = YES).number = #max.rounds.number"];

Core Data Fetching Properties with Group By Count

This is the SQL version of the query I would like to write for Core Data:
SELECT Group.Name, COUNT(Item.Name)
FROM Item INNER JOIN Group ON Item.GroupID = Group.ID
GROUP BY Group.Name
So far what I have is:
NSFetchRequest* fetchGroupSummary = [NSFetchRequest fetchRequestWithEntityName:#"Item"];
NSEntityDescription* entity = [NSEntityDescription entityForName:#"Item" inManagedObjectContext:[[CoreDataManager sharedInstance] managedObjectContext]];
NSAttributeDescription* groupName = [entity.relationshipsByName objectForKey:#"group"];
NSExpression *countExpression = [NSExpression expressionForFunction: #"count:" arguments: [NSArray arrayWithObject:[NSExpression expressionForKeyPath: #"name"]]];
NSExpressionDescription *expressionDescription = [[NSExpressionDescription alloc] init];
[expressionDescription setName: #"count"];
[expressionDescription setExpression: countExpression];
[expressionDescription setExpressionResultType: NSInteger32AttributeType];
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"group.sort" ascending:YES];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"group.stage == %#", stage];
[fetchGroupSummary setEntity:entity];
[fetchGroupSummary setSortDescriptors:#[sortDescriptor]];
[fetchGroupSummary setPropertiesToFetch:[NSArray arrayWithObjects:groupName, expressionDescription, nil]];
[fetchGroupSummary setPropertiesToGroupBy:[NSArray arrayWithObject:groupName]];
[fetchGroupSummary setResultType:NSDictionaryResultType];
[fetchGroupSummary setPredicate:predicate];
NSError* error = nil;
groups = [[[CoreDataManager sharedInstance] managedObjectContext] executeFetchRequest:fetchGroupSummary error:&error];
expressionDescription = nil;
This almost gives me everything, however instead of groupName being the group relationship I would like to specify group.name - is this possible?
Mark
setPropertiesToGroupBy of NSFetchRequest accepts an array of NSPropertyDescription or NSExpressionDescription objects, or keypath strings. In your case, you can use a keypath string:
[fetchGroupSummary setPropertiesToGroupBy:[NSArray arrayWithObject:#"group.name"]];

Fetching a maximum value with CoreData

This is the first time I'm trying to use CoreData and I'm having some trouble getting the max date from a a table.
This is basically what I'm trying to do:
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Radar" inManagedObjectContext:managedObjectContext];
[request setEntity:entity];
NSExpression *keyPathExpression = [NSExpression expressionForKeyPath:#"modifiedOn"];
NSExpression *maxDate = [NSExpression expressionForFunction:#"max:" arguments:[NSArray arrayWithObject:keyPathExpression]];
NSExpressionDescription *expressionDescription = [[NSExpressionDescription alloc] init];
[expressionDescription setName:#"maxDate"];
[expressionDescription setExpression:maxDate];
[expressionDescription setExpressionResultType:NSDateAttributeType];
[request setPropertiesToFetch:[NSArray arrayWithObject:expressionDescription]];
NSError *error = nil;
NSArray *objects = [managedObjectContext executeFetchRequest:request error:&error];
if (objects == nil) {
// Handle the error.
}
else {
if ([objects count] > 0) {
NSLog(#"%d", [objects count]);
NSLog(#"MAx date: %#", [[objects objectAtIndex:0] valueForKey:#"modifiedOn"]);
}
}
This code somehow does the opposite of what I want and print the minimum date. Since I've just started with CoreData it's obvious that I got something wrong, but what?
Maybe you should try to NSLog the [objects lastObject],instead of first one?

an NSPredicate for selecting a row with maximum value by group

I'm using Core Data to store entities in the form
TrackerEntry
username
timestamp
I want to select the latest record for each user. Using sql it would be something like
SELECT MAX(timestamp) FROM Log GROUP BY username
Is there anyway to create an NSPredicate to do this?
I would do it using NSExpression. This bit of code below won't work for you because you will have to group by username too, but it's a start for the best way of doing this without having to fetch everything. You want to perform the max and group in the db, not in memory - as it will be faster:
NSFetchRequest *fetchRequest = [[[NSFetchRequest alloc] init] autorelease];
// Expression for the max
NSExpression *keyPathExpression = [NSExpression expressionForKeyPath:#"timestamp"];
NSEntityDescription *entity = [NSEntityDescription entityForName:entityName
inManagedObjectContext:self.coreDataStack.managedObjectContext];
[fetchRequest setEntity:entity];
[fetchRequest setResultType:NSDictionaryResultType];
NSExpression *valueSumExpression = [NSExpression expressionForFunction:#"max:" arguments:[NSArray arrayWithObject:keyPathExpression]];
NSExpressionDescription *expressionDescription = [[[NSExpressionDescription alloc] init] autorelease];
[expressionDescription setName:#"maxTimestamp"];
[expressionDescription setExpression:valueSumExpression];
[expressionDescription setExpressionResultType:NSDecimalAttributeType];
[fetchRequest setPropertiesToFetch:[NSArray arrayWithObject:expressionDescription]];
// Filter
//NSPredicate *pred = [NSPredicate predicateWithFormat:#" your predicate here"];
//fetchRequest.predicate = pred;
NSArray *results = [self.coreDataStack.managedObjectContext executeFetchRequest:fetchRequest error:nil];
if([results count] == 0) {
} else {
// [[results objectAtIndex:0] valueForKey:#"maxTimestamp"];
}
This worked for me, but it is a loop. First you have to get the unique names into an array.
NSFetchRequest *nameRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Log" inManagedObjectContext:self.managedObjectContext];
nameRequest.entity = entity;
nameRequest.resultType = NSDictionaryResultType;
[nameRequest setPropertiesToFetch:[NSArray arrayWithObject:[[entity propertiesByName] objectForKey:#"name"]]];
NSArray *allNames = [self.managedObjectContext executeFetchRequest:nameRequest error:nil];
names = [allNames valueForKeyPath:#"#distinctUnionOfObjects.name"];
[nameRequest release];
NSLog(#"%#", names);
Not very efficient or convenient. After all, Core Data is not a database but an object graph.
Then you can loop through these and fetch just the top one.
NSMutableArray *mutableResults = [NSMutableArray array];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
request.entity = [NSEntityDescription entityForName:#"Log" inManagedObjectContext:self.managedObjectContext];
NSSortDescriptor *numberSort = [NSSortDescriptor sortDescriptorWithKey:#"number" ascending:NO];
request.sortDescriptors = [NSArray arrayWithObject:numberSort];
request.fetchLimit = 1;
for (NSString *s in names) {
NSPredicate *pred = [NSPredicate predicateWithFormat:#"name = %#", s];
request.predicate = pred;
NSArray *results = [self.managedObjectContext executeFetchRequest:request error:nil];
[mutableResults addObject:[results objectAtIndex:0]];
}
[request release];
NSLog(#"%#", mutableResults);

Expression for function count in NSFetchRequest returns one less than regular fetch

In my first attempt at using NSExpression in a fetch request, I’m getting a result that is consistently one off from what I get if I use a regular fetch request.
The MO “Subject” has a to-many relationship to the MO “Book,” the inverse being to-one.
This is the NSExpression fetchRequest I’m using:
Project_AppDelegate *appDelegate = [[NSApplication sharedApplication] delegate];
NSManagedObjectContext *context = [appDelegate managedObjectContext];
NSEntityDescription *entityDescription = [NSEntityDescription entityForName:#“Book”
inManagedObjectContext:context];
Subject *subjectToDelete = [self.arrayOfSubjects objectAtIndex:indexSelected];
NSPredicate *pred = [NSPredicate predicateWithFormat:#"subject == %#", subjectToDelete];
NSExpression *expn = [NSExpression expressionForFunction:#"count:"
arguments:[NSArray arrayWithObject:[NSExpression expressionForKeyPath:#"idPerm"]]];
NSExpressionDescription *expnDesc = [[NSExpressionDescription alloc] init];
[expnDesc setExpression:expn];
[expnDesc setName:#“countMatchingBooks”];
[expnDesc setExpressionResultType:NSInteger64AttributeType];
NSArray *properties = [NSArray arrayWithObject:expnDesc];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entityDescription];
[request setPredicate:pred];
[request setPropertiesToFetch:properties];
[request setResultType:NSDictionaryResultType];
NSError *error = nil;
NSArray *results = [context executeFetchRequest:request error:&error];
if (error) {
// error handling here
}
[request release];
[expnDesc release];
// Retrieve the count from the results array.
NSNumber *numBooksAssignedSubjectToDelete = [[results objectAtIndex:0] valueForKey:#“countMatchingBooks”];
uint64_t uloloBooksAssignedSubjectToDelete = [numBooksAssignedSubjectToDelete unsignedLongLongValue];
(The idea is to present the user with a confirmation panel advising them of how many Books will be deleted via the Cascade rule if they choose to delete the chosen Subject — without faulting the Book MOs at this point.)
And this is the simple fetchRequest I’m using as a test:
NSEntityDescription *entityDescription = [NSEntityDescription entityForName:#“Book”
inManagedObjectContext:contextMain];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entityDescription];
NSError *error = nil;
NSArray *booksAll = [contex executeFetchRequest:request error:&error];
[request release];
// Loop through the “booksAll” array and count those whose subject matches the one assigned as “subjectToDelete”
What happens is that if the NSExpression fetchRequest returns a count of n, the simple fetchRequest returns a count of n + 1.
Thinking the fetchRequests themselves might be somehow altering the data, I tried running them in a different order, but with the same result.
Maybe requests using expressions skip MOs which have not yet been saved? No. I ran a test that creates a bunch of new “Book” MOs to see if the gap between expression request and regular request would widen. It remained exactly one off.
Any idea what I’m doing wrong?
NSFetchRequests using NSExpressionDescription does not include unsaved objects. NSFetchRequest has a method -setIncludePendingChanges:, which does not accept YES when the result type is NSDictionaryResultType. This means that you cannot use NSExpressionDescription to get unsaved objects.