I have an entity called Rounds that has basic data about golf rounds. I am trying to calculate the number of rounds as well as the average score. However, every time I try and calculate these values it returns 0 (zero). There are no errors, and no crashes.
I have the following function in Rounds.m:
+(NSNumber *)aggregateOperation:(NSString *)function onAttribute:(NSString *)attributeName withPredicate:(NSPredicate *)predicate inManagedObjectContext:(NSManagedObjectContext *)context
{
NSExpression *ex = [NSExpression expressionForFunction:function
arguments:[NSArray arrayWithObject:[NSExpression expressionForKeyPath:attributeName]]];
NSExpressionDescription *ed = [[NSExpressionDescription alloc] init];
[ed setName:#"result"];
[ed setExpression:ex];
[ed setExpressionResultType:NSInteger64AttributeType];
NSArray *properties = [NSArray arrayWithObject:ed];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setPropertiesToFetch:properties];
[request setResultType:NSDictionaryResultType];
if (predicate != nil)
[request setPredicate:predicate];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Rounds"
inManagedObjectContext:context];
[request setEntity:entity];
NSArray *results = [context executeFetchRequest:request error:nil];
NSDictionary *resultsDictionary = [results objectAtIndex:0];
NSNumber *resultValue = [resultsDictionary objectForKey:#"result"];
return resultValue;
}
I then call this method from my view controller to set the values of the labels for number of rounds and scoring average:
-(NSNumber*) scoringAverageCalc
{
NSNumber *scoreAverage = [Rounds aggregateOperation:#"average:" onAttribute:#"roundScore" withPredicate:nil inManagedObjectContext:_managedObjectContext];
return scoreAverage;
}
-(NSNumber*)countOfRounds
{
NSNumber *roundCount = [Rounds aggregateOperation:#"count:" onAttribute:#"roundDate" withPredicate:nil inManagedObjectContext:_managedObjectContext];
return roundCount;
}
Can someone please tell me why I am not getting the correct value?
I think NSExpression is overkill for simple sums. I would do it like this: fetch your rounds as normal managed objects (NSManagedObjectResultType) and then use KVC which should have just the aggregators you need.
NSNumber *sum = [rounds valueForKeyPath:#"#sum.score"];
NSNumber *avg = [rounds valueForKeyPath:#"#avg.score"];
Simple, isn't it? Check it out here.
Your code works for me. Are you sure there are no misspellings in your entity or attribute names?
Related
This code currently lists all my categories. However I will like to filter this result to display specific results. For example... The array contains the items Jamaica, Japan, Germany and Asia. I will like to filter the displayed results to only show Japan and Germany. I've read NSPredicate can assist me but i'm not too sure on how to implement it here.
Datacontroller.M
+(NSArray*) getCategories {
AppDelegate* delegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
NSManagedObjectContext* context = delegate.managedObjectContext;
NSError *error;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setReturnsObjectsAsFaults:NO];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"StoreCategory" inManagedObjectContext:context];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"created_at" ascending:YES];
[fetchRequest setSortDescriptors:#[sortDescriptor]];
[fetchRequest setEntity:entity];
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
return fetchedObjects;
}
Categories.m
-(UITableViewCell*)MGListView:(MGListView *)listView1 didCreateCell:(MGListCell *)cell indexPath:(NSIndexPath *)indexPath {
if(cell != nil) {
StoreCategory* cat = [listViewMain.arrayData objectAtIndex:indexPath.row];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.backgroundColor = [UIColor clearColor];
[cell.labelTitle setText:cat.category];
[self setImage:cat.category_icon imageView:cell.imgViewThumb];
}
return cell;
}
You cannot filter the unwanted category in this method because indexPath will be a sequence of indices.
You will have to build another array that includes the category you want before UITableView delegate or DataSource functions are called, for example, in ViewDidLoad.
This method seemed to do the trick.
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"category CONTAINS[cd] %# OR category CONTAINS[cd] %#", #"Japan", #"Germany", context];
[fetchRequest setPredicate:predicate];
I am new to core data and am still trying to get my head around it. I have a problem that I have spent hours on trying to solve. I just can't seem to solve it and was hoping someone could help me.
I can't post images yet so here is a link to my data model:
http://yourpcsolution.net/model.png
Each relationship has an inverse. A Game can have many Periods and a Period can have many Players. A Player can be in more than one Period.
Here is my code for populating DB:
- (void) prepareDB {
Games *game = [NSEntityDescription insertNewObjectForEntityForName:#"Games" inManagedObjectContext:self.managedObjectContext];
game.date = [NSDate date];
game.name = #"Game 1";
Periods *period = [NSEntityDescription insertNewObjectForEntityForName:#"Periods" inManagedObjectContext:self.managedObjectContext];
period.date = [NSDate date];
period.name = #"1st Period";
NSMutableSet *gameSet = [[NSMutableSet alloc] initWithObjects:period, nil];
game.periods = gameSet;
Players *player = [NSEntityDescription insertNewObjectForEntityForName:#"Players" inManagedObjectContext:self.managedObjectContext];
player.name = #"Player1";
NSMutableSet *playerSet = [[NSMutableSet alloc] initWithObjects:player, nil];
period.players = playerSet;
NSError *error;
if (![self.managedObjectContext save:&error]) {
NSLog(#"Whoops, couldn't save: %#", [error localizedDescription]);
}
}
I think that's the correct way to add data because when looking at the sql db the data is there. My problem is in trying to retrieve the players that belong to each period. Because Periods is a set in Games and Players is a set in Periods in core data. So I can't retrieve players by going: game.periods.players
Here is my code for retrieving a Period and in the xcode log i am getting a fault:
"Relationship 'players' fault on managed object (0x8e72bc0) <Periods: 0x8e72bc0>
(entity: Periods; id: 0x8e727c0 <x-coredata://7F63902B-FCB6-4ACA-BB40-904755D37A4A/Periods/p1>;
data: {\n date = \"2013-07-09 19:35:48 +0000\";\n
games = \"0x8e6d760 <x-coredata://7F63902B-FCB6-4ACA-BB40-904755D37A4A/Games/p1>\";\n
name = \"1st Period\";\n players = \"<relationship fault: 0x9840330 'players'>\";\n})"
My Code for retrieving a Period:
NSString *firstPeriod = #"1st Period";
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"name == %#", firstPeriod];
[request setEntity:[NSEntityDescription entityForName:#"Periods" inManagedObjectContext:self.managedObjectContext]];
[request setPredicate:predicate];
NSArray *period = [self.managedObjectContext executeFetchRequest:request error:&error];
I'm not sure how to proceed from here to retrieve the players. I know how to retrieve what Periods belong to each game but I don't know how to retrieve the players that belong to a period. I have been trying for days to figure this out and it's just not working.
Is my data model correct? How should I be retrieving the data? Any help or advice is appreciated.
To fetch all players of a given period, the following should work:
NSString *firstPeriod = #"1st Period";
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:[NSEntityDescription entityForName:#"Players" inManagedObjectContext:self.managedObjectContext]];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"ANY periodsin.name == %#", firstPeriod];
[request setPredicate:predicate];
NSArray *players = [self.managedObjectContext executeFetchRequest:request error:&error];
"ANY" in the predicate is necessary because each player is connected to multiple Periods.
Remark: The data type of a to-many relationship is NSSet, not NSMutableSet.
ADDED: To fetch all periods for a given player, you can use the following fetch request:
Players *givenPlayer = ... ; // the given player
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:[NSEntityDescription entityForName:#"Periods" inManagedObjectContext:self.managedObjectContext]];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"ANY players == %#", givenPlayer];
[request setPredicate:predicate];
NSArray *periods = [self.managedObjectContext executeFetchRequest:request error:&error];
Alternatively, you can access the relationship directly:
Players *givenPlayer = ... ; // the given player
NSArray *periods = [givenPlayer.periodsin allObjects]; // "allObjects" converts NSSet to NSArray
How do I setup a fetch request to only pull the data from an entity's attribute with one particular value? This is the basic code I've used before.
-(void)fetchResults
{
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:self.entityDescription.name];
NSString *cacheName = [self.entityDescription.name stringByAppendingString:#"Cache"];
// predicate code
if (self.predicate != nil) {
[fetchRequest setPredicate:self.predicate];
}
// end of predicate code
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"name" ascending:YES];
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:sortDescriptor]];
self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:cacheName];
BOOL success;
NSError *error;
success = [self.fetchedResultsController performFetch:&error];
if (!success)
{
NSLog(#"%#", [error localizedDescription]);
}
}
I've been looking at this page: http://bit.ly/KevYwR is this the right direction?
Do I need to use NSPredicate or can I do without?
Thanks for any help, point in the right direction, etc.
Setting up a NSFetchRequest is equivalent to a SELECT statetement in SQL language.
Here a simple example:
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:[NSEntityDescription entityForName:#"EntityName" inManagedObjectContext:moc]];
NSError *error = nil;
NSArray *results = [moc executeFetchRequest:request error:&error];
// error handling code
The array results contains all the managed objects contained within the sqlite file. If you want to grab a specific object (or more objects) you need to use a predicate with that request. For example:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"attribute == %#", #"Some Value"];
[request setPredicate:predicate];
In this case results contains the objects where attribute is equal to Some Value. Setting a predicate is equal to put the WHERE clause in a SQL statement.
Note
I suppose that the name of the entity is EntityName and its property is called attribute which is of type string.
For further info I suggest you to read the Core Data programming guide and NSFecthRequest class reference.
http://developer.apple.com/library/iOS/#documentation/Cocoa/Conceptual/CoreData/cdProgrammingGuide.html
https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/CoreDataFramework/Classes/NSFetchRequest_Class/NSFetchRequest.html
Hope it helps.
I have a Entity wich has a toMany relationship to another one. In this second one I have an attribute called "versionNumber" so. I have an object on entity type A, and I want to get the related entity B which has the biggest (max) versionNumber.
I have the following but that returns me a result obtained over all records on entity B, not over the specific entities related to the object of type A.
NSInteger vNumber = 0;
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:DPA_VERSION_KEY inManagedObjectContext:[self managedObjectContext]];
[request setEntity:entity];
// Specify that the request should return dictionaries.
[request setResultType:NSDictionaryResultType];
NSExpression *keyPathExpression = [NSExpression expressionForKeyPath:VERSION_NUMBER_KEY];
NSExpression *maxNumberExpression = [NSExpression expressionForFunction:#"max:"
arguments:[NSArray arrayWithObject:keyPathExpression]];
NSExpressionDescription *expressionDescription = [[NSExpressionDescription alloc] init];
[expressionDescription setName:#"maxNumber"];
[expressionDescription setExpression:maxNumberExpression];
[expressionDescription setExpressionResultType:NSDecimalAttributeType];
[request setPropertiesToFetch:[NSArray arrayWithObject:expressionDescription]];
// Execute the fetch.
NSError *error = nil;
NSArray *objects = [[self managedObjectContext] executeFetchRequest:request error:&error];
if (objects == nil) {
// Handle the error.
}
else {
if ([objects count] > 0) {
vNumber = [[[objects objectAtIndex:0] valueForKey:#"maxNumber"] integerValue] +1;
}
}
[expressionDescription release];
[request release];
return vNumber;
I have an idea but I hadn't been able to materialize it. I must ask SELF which is my object A to do that fetch over its relationship toVersions (Entity B).
Thanks for the help.
G.
Set a predicate to limit the request to only those B objects who have a relationship with A.
[request setPredicate:[NSPredicate predicateWithFormat:#"myA == %#", myA];
Could someone please help me define a predicate that returns only NSManagedObject's who's "letters" attribute length is within a certain range?
Here's the example I've been trying, I've got a feeling it's the letters.length notation, I've also tried the kvc letters.#length with no success.. What am I doing wrong?
NSManagedObjectContext *context = ...;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Word" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"letters" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"letters.length BETWEEN %#", [NSArray arrayWithObjects: [NSNumber numberWithInt:5], [NSNumber numberWithInt:20], nil]];
[fetchRequest setPredicate:predicate];
NSUInteger limit = 20;
[fetchRequest setFetchLimit:limit];
NSError *error = nil;
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
Not sure how this code snippet is performance wise but here is my answer to your question:
NSString *attributeName = #"letters";
NSString *attributeValue = [NSString stringWithFormat:#"'.{%d,%d}'", 5, 20];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"%K MATCHES %#", attributeName, attributeValue];
[fetchRequest setPredicate:predicate];
Joss.
I've got some bad news. You can't do this. There is no length attribute for strings in the NSPredicate (that I've been able to find).
What I would recommend you do is add an attribute for the length of the stored string in your managed object that gets set when you set the letters attribute. You can then do your query on that attribute and return the values you want.
In case someone is looking for a way to test whether a string has length using an NSPredicate,
it is possible.
Swift:
returns strings with 4 to 7 characters.
NSPredicate(format: "text MATCHES %#", ".{4,7}")