NSFetchRequest with distinct properties - iphone

I'm trying to get a distinct result from NSPredicate.
My code:
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Members" inManagedObjectContext:context];
request.entity = entity;
request.sortDescriptors = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:#"city"
ascending:YES
selector:#selector(caseInsensitiveCompare:)]];
request.predicate = [NSPredicate predicateWithFormat:#"memberDeleted == %#", [NSNumber numberWithBool:NO]];
NSDictionary *properties = [entity propertiesByName];
request.propertiesToFetch = [NSArray arrayWithObject:[properties objectForKey:#"city"]];
request.returnsDistinctResults = YES;
request.fetchBatchSize = 20;
NSFetchedResultsController *frc = [[NSFetchedResultsController alloc] initWithFetchRequest:request
managedObjectContext:context
sectionNameKeyPath:nil
cacheName:#"CityCache"];
[request release];
self.fetchedResultsController = frc;
[frc release];
The issue is that the result returns many times the same City. This Entity has a lot of Members, and each Member has a property "city".
What am I doing wrong?
Thanks,
RL

Make sure to set the resultType of the NSFetchRequest to NSDictionaryResultType. The default is to return the actual objects, and so it will ignore propertiesToFetch.

With the help of #Alex, here's the final code, without FRC:
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Members" inManagedObjectContext:self.managedObjectContext];
request.entity = entity;
request.sortDescriptors = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:#"city"
ascending:YES
selector:#selector(caseInsensitiveCompare:)]];
request.predicate = [NSPredicate predicateWithFormat:#"memberDeleted == %#", [NSNumber numberWithBool:NO]];
NSDictionary *properties = [entity propertiesByName];
request.propertiesToFetch = [NSArray arrayWithObject:[properties objectForKey:#"city"]];
request.returnsDistinctResults = YES;
request.resultType = NSDictionaryResultType;
request.fetchBatchSize = 20;
NSError *error = nil;
NSArray *tempArray = [self.managedObjectContext executeFetchRequest:request error:&error];
NSMutableArray *cities = [[NSMutableArray alloc] init];
for (int i=0; i < [tempArray count]; i++){
NSDictionary *tempDict = [NSDictionary dictionary];
tempDict = [tempArray objectAtIndex:i];
if ([tempDict objectForKey:#"city"] != nil)
[cities addObject:[tempDict objectForKey:#"city"]];
}
//if the tempArray has no nil values, it's more efficient with:
//NSArray* cities = [tempArray valueForKeyPath:#"city"];
self.cityArray = cities;
[cities release];
[request release];
This returns a NSArray with the list of the Cities.
Thanks.

From the official documentation:
NSManagedObjectContext *context = <#Get the context#>;
NSEntityDescription *entity = [NSEntityDescription entityForName:#"<#Entity name#>" inManagedObjectContext:context];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entity];
[request setResultType:NSDictionaryResultType];
[request setReturnsDistinctResults:YES];
[request setPropertiesToFetch :[NSArray arrayWithObject:#"<#Attribute name#>"]];
// Execute the fetch.
NSError *error;
id requestedValue = nil;
NSArray *objects = [managedObjectContext executeFetchRequest:request error:&error];
if (objects == nil) {
// Handle the error.
}

Related

How to get the sm_owner User object using StackMob as backend

I find out the User has a sm_owner field. But how do I get the user using sm_owner. My code is like this:
[self.client getLoggedInUserOnSuccess:^(NSDictionary *result) {
NSString *currentLogginedUser = [result valueForKey:#"sm_owner"];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"User" inManagedObjectContext:self.managedOjbectContext];
[request setEntity:entity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"sm_owner like user/%#", currentLogginedUser];
[request setPredicate:predicate];
self.user = [[self.managedOjbectContext executeFetchRequest:request error:nil] objectAtIndex:0];
} onFailure:^(NSError *error) {
}];
And the crash message is:reason: 'Unimplemented SQL generation for predicate (sm_owner LIKE user / "user/test")'
I'm the platform Evangelist for StackMob.
The result from the getLoggedInUserOnSuccess method normally contains primary key "username". So the following code should work.
[self.client getLoggedInUserOnSuccess:^(NSDictionary *result) {
NSString *currentLogginedUser = [result objectForKey:#"username"];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"User" inManagedObjectContext:self.managedOjbectContext];
[request setEntity:entity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"username == %#", currentLogginedUser];
[request setPredicate:predicate];
self.user = [[self.managedOjbectContext executeFetchRequest:request error:nil] objectAtIndex:0];
} onFailure:^(NSError *error) {
}];
Alternatively, if you rather grab the sm_owner from the NSDictionary result. I would do the following.
NSString *sm_owner = [result valueForKey:#"sm_owner"];
NSArray *myArray = [sm_owner componentsSeparatedByString:#"/"];
NSString *currentLogginedUser = [myArray objectAtIndex:1];
Then perform your fetch.

How to sort a fetch in Core Data

I'm doing a fetch of data to show it in a UITableView, I do the fetch but I want to sort them by date of creation or by any sort method.
Here is the method:
-(NSArray *) lesStations {
NSManagedObjectContext *moc = [[AppDelegate sharedAppDelegate]managedObjectContext];
NSFetchRequest *fetch = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Station" inManagedObjectContext:moc];
[fetch setEntity:entity];
NSError *error;
NSArray *result = [moc executeFetchRequest:fetch error:&error];
if (!result) {
return nil;
}
return result;
}
This should work
-(NSArray *) lesStations {
NSManagedObjectContext *moc = [[AppDelegate sharedAppDelegate]managedObjectContext];
NSFetchRequest *fetch = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Station" inManagedObjectContext:moc];
[fetch setEntity:entity];
NSError *error;
NSArray *result = [moc executeFetchRequest:fetch error:&error];
if (!result) {
return nil;
}
NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:"creationDate" ascending:YES];
NSArray *sortedResults = [result sortedArrayUsingDescriptors:[NSArray arrayWithObject:sort]];
[sort release];
return sortedResults;
}
You have 2 options: get the results sorted or sort the NSArray with the results:
NSFetchRequest could be set a NSArray of NSSortDescriptor
NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:#"place" ascending:NO]; //the key is the attribute you want to sort by
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:sort]];
You can also sort the returned NSArray (see all instance methods of NSArray starting with sortedArray...)
I suggest using the first approach when working with CoreData. The second one is just informative when working with arrays...
You can sort fetch results by setting sortDescriptors for fetch, e.g.
fetch.sortDescriptors = [NSArray arrayWithObjects:[[NSSortDescriptor alloc] initWithKey: ascending: selector:], nil];
NSError *error;
NSArray *result = [moc executeFetchRequest:fetch error:&error];

Record data to attributes in core data Iphone

I have about 20 entity : Event1 ... Event20
In each entity i have property values1 ... values20
I need record data in for example Event5 values8
But something wrong - may be data record in each values8 (Event1 ... Event20)
How do right?
NSString *str = [NSString stringWithFormat:#"Event%d", variable];
NSString *value = [NSString stringWithFormat:#"values%d", vari];
TermometrAppDelegate *app;
app = (TermometrAppDelegate *)[UIApplication sharedApplication].delegate;
NSFetchRequest *fetchRequests = [[NSFetchRequest alloc] init];
NSEntityDescription *entit = [NSEntityDescription entityForName:str inManagedObjectContext:app.managedObjectContext];
[fetchRequests setEntity:entit] ;
[fetchRequests setPropertiesToFetch :[NSArray arrayWithObject:value]];
[fetchRequests setReturnsDistinctResults:YES];
NSError *error;
NSArray *fetchedObject = [app.managedObjectContext executeFetchRequest:fetchRequests error:&error];
NSManagedObject *fetched = nil;
NSManagedObject *fetch = nil;
printf("\n%d", [fetchedObject count]);
The code below retrieves values in Entity5 for Value8 equal to someValueForEight.text (UITextField). This should help you out.
TermometrAppDelegate *app;
app = (TermometrAppDelegate *)[UIApplication sharedApplication].delegate;
NSManagedObjectContext *context = [app managedObjectContext];
NSEntityDescription *entityDesc = [NSEntityDescription entityForName:#"Entity5" inManagedObjectContext:context];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entityDesc];
NSPredicate *pred = [NSPredicate predicateWithFormat:#"(value8 = %#)", someValueForEight.text];
[request setPredicate:pred];
NSManagedObject *matches = nil;
NSError *error;
NSArray *objects = [context executeFetchRequest:request error:&error];

How to count in coredata (aggregation)?

I am learning core data and particularly working on aggregation.
Current what I want to do : count the number of records from the table which is in to-many relationship with inverse relationship on some criteria.
Currently I am doing this :
NSExpression *ex = [NSExpression expressionForFunction:#"count:"
arguments:[NSArray arrayWithObject:[NSExpression expressionForKeyPath:#"ddname"]]];
NSPredicate *pred = [NSPredicate predicateWithFormat:#"ddtype == 'Home'"];
NSExpressionDescription *ed = [[NSExpressionDescription alloc] init];
[ed setName:#"countDDEvents"];
[ed setExpression:ex];
[ed setExpressionResultType:NSInteger16AttributeType];
NSArray *properties = [NSArray arrayWithObject:ed];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setPredicate:pred];
[request setPropertiesToFetch:properties];
[request setResultType:NSDictionaryResultType];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"DDEvent" inManagedObjectContext:[self.currentAccount managedObjectContext]];
[request setEntity:entity];
NSArray *results = [[self.currentAccount managedObjectContext] executeFetchRequest:request error:nil];
NSDictionary *dict = [results objectAtIndex:0];
NSLog(#"Average birthdate for female heroes: %#", [dict objectForKey:#"countDDEvents"]);
Its from jeff lemarche.
EDIT : and I have found my solution as
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSPredicate *pred = [NSPredicate predicateWithFormat:#"ddtype == 'Home'"];
[request setPredicate:pred];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"DDEvent" inManagedObjectContext:[self.currentAccount managedObjectContext]];
[request setEntity:entity];
NSError *error = nil;
NSUInteger count = [[self.currentAccount managedObjectContext] countForFetchRequest:request error:&error];
It is working nicely .But I want to do more request of such type at a time . So i think this can't be a preferred way of getting the count .
EDIT :
So I think the approach would be the appropriate one ????
So can anyone tell me more efficient an preferred way of doing this .
Thanks .
I had to count about 10 000 entities and it slowed down my interface responsiveness a lot while doing it with countForFetchRequest..
Here is a way of doing it wth NSExpression:
- (NSUInteger) unfilteredFCsCount {
// Just the fetchRequest
NSFetchRequest *fetchRequest = [self unfilteredFCsFetchRequest];
[fetchRequest setResultType: NSDictionaryResultType];
// You can use any attribute of the entity. its important, because you are not counting
// the properties, but actually the entities
NSExpression *keyPathExpression = [NSExpression expressionForKeyPath: #"sortIndex_"]; // Does not really matter
NSExpression *maxExpression = [NSExpression expressionForFunction: #"count:"
arguments: [NSArray arrayWithObject:keyPathExpression]];
NSExpressionDescription *expressionDescription = [[NSExpressionDescription alloc] init];
[expressionDescription setName: #"fcCount"];
[expressionDescription setExpression: maxExpression];
[expressionDescription setExpressionResultType: NSInteger32AttributeType];
[fetchRequest setPropertiesToFetch: [NSArray arrayWithObject:expressionDescription]];
NSUInteger fcCount = 0;
NSError *error = nil;
NSArray *results = nil;
results = [self.managedObjectContext executeFetchRequest: fetchRequest error: &error];
KSLog(KSLogLevelDebug, #"unfilteredFCsCount results: %#", results);
if([results count] > 0) {
NSNumber *count = [[results objectAtIndex: 0] objectForKey: #"fcCount"];
fcCount = [count intValue];
}
return fcCount;
}
Jeff LaMarche is just using this as a simple example. In practice, this need is so common that Key-Value Coding has a built in macro to handle it and other common collection operations.
See: The Key-Value Programming Guide: Set and Array Operators
In this case you would use the #count operator in your predicate.
Of course, hand tuning your own expression gives you fine control over your predicates but the operators handle 80% of such task.

Fetch Data using predicate. Retrieve single value

I want to retrieve a single value (String) from "Entry." I believe I am missing something. Can someone point it out?
XYZAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *managedObjectContext = appDelegate.managedObjectContext;
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"name == %#", entryToSearchFor];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Entry" inManagedObjectContext:managedObjectContext];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"name" ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[request setSortDescriptors:sortDescriptors];
[request setEntity: entity];
[request setPredicate: predicate];
NSArray *results = [managedObjectContext executeFetchRequest:request error:nil];
if (results == nil) {
NSLog(#"No results found");
}else {
NSLog(#"entryToSearchFor %#", entryToSearchFor);
NSLog(#"results %#", [results objectAtIndex:0]);
}
Btw, the NSLog results outputs the following:
results <NSManagedObject: 0x3d2d360> (entity: Entry; id: 0x3d13650 <x-coredata://6EA12ADA-8C0B-477F-801C-B44FE6E6C91C/Entry/p3> ; data: <fault>)
NSFetchRequest will return an array of NSManagedObjects that matches the request. That is consistent with what you are seeing. Assuming you want to fetch the name, you can do NSLog(#"the name is %#", [[results objectAtIndex:0] name]); to print out the name.