How to extract data from a NSArray fill with NSDictionary? - iphone

i try to make a kind of "sql distinct" request with core data,
so i set up my NSFetchrequest like this
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity:[NSEntityDescription entityForName:#"my_table" inManagedObjectContext:managedObjectContext]];
[fetchRequest setResultType:NSDictionaryResultType];
[fetchRequest setReturnsDistinctResults:YES];
[fetchRequest setPropertiesToFetch:[NSArray arrayWithObjects:#"first_property",#"second_property",nil]];
// Execute the fetch
NSError *error;
table_dom = [[NSArray alloc] init];
table_dom = [managedObjectContext executeFetchRequest:fetchRequest error:&error];
[fetchRequest release];
this looks ok for me but I think I retrieve an NSArray fill with NSDictionary, is that rigth ?
So my question is how can I extract data from table_dom for a tableview, I try many things but no ones work ?
It has to be:
cell.textLabel.text = //"first property" and
cell.detailTextLabel.text = //"second_property"
Or perhaps to make it simple, can I retrieve objects with setReturnsDistinctResults instead of NSDictionary ?
Thanks

Why not you can access in forms of objects so for this
remove
[fetchRequest setResultType:NSDictionaryResultType];
and
[fetchRequest setPropertiesToFetch:[NSArray arrayWithObjects:#"first_property",#"second_property",nil]];
then Now
table_dom = [managedObjectContext executeFetchRequest:fetchRequest error:&error];
table_dom having objects of your entity so where you use this array
extract objects and access both property first_property and second_property
by simply using . operator.

You are correct that you will get a NSArray filled with NSDictionary. First I would like to point out you have a memory leak in your code. table_dom = [[NSArray alloc] init]; is not necessary since executeFetchRequest:error: will return an NSArray. So just remove that line.
After you have your array you can do something like this:
NSDictionary *dict = [table_dom objectAtIndex:indexPath.row];
cell.textLabel.text = [dict objectForKey:#"first_property"];
cell.detailTextLabel.text = [dict objectForKey:#"second_property"];

Related

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.

iphone core data save not working

Im trying to save a list of Device classes (a custom class) using Core Data and retrieve it. But After I save it, my query, which is VERY simple, doesnt return any records.
My call to save the records always returns YES and no errors:
BOOL resultOfSave = [managedObjectContext save:&err];
My predicate for searching by the property userLogin is like so:
- (NSArray *) devicesForUser:(NSString *)username
{
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Device" inManagedObjectContext:managedObjectContext];
[request setEntity:entity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"userLogin = %#", username];
[request setPredicate:predicate];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"macAddress" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[request setSortDescriptors:sortDescriptors];
[sortDescriptors release];
[sortDescriptor release];
NSError *error = nil;
NSArray *fetchResults = [managedObjectContext executeFetchRequest:request error:&error];
if (fetchResults == nil) {
// Handle the error.
NSLog(#"ERROR: nil fetch result array");
}
[request release];
NSLog(#"devicesForUser [%#] count %d", username, [fetchResults count]);
return fetchResults;
}
but I always get zero results, if I get rid of the predecate, I get all the objects, and if I loop through them I can see that the userLogin property for at least one of the object IS set to the username that I pass in...
Has anyone ever had an issue like this???
Thanks for any help you can give
Mark
In your predicate, change:
#"userLogin = %#"
...to:
#"userLogin == %#"

NSArray from NSSet - Do I have to sort it myself?

I've got data in an NSSet, and I need to get it into an NSArray.
Do I need to sort it myself (again, this came from Core Data) or can I get it out in a sorted order?
You can specify a sort when you retrieve data with an NSFetchRequest by setting the sortDescriptors property to an array of NSSortDescriptors. But if you already have it in an NSSet and don't want to make another fetch request, you can use:
[[theSet allObjects] sortedArrayUsingDescriptors:sortDescriptors];
It'll create an interim NSArray when you call allObjects and then another sorted array afterwards, so it's not 100% efficient, but the overhead should be negligible for reasonably-sized data sets (and certainly less than the cost of sorting).
Edit
Actually, I was wrong - NSSet has the sortedArrayUsingDescriptors: method too. So you can just call [theSet sortedArrayUsingDescriptors:descriptors] and do it all in one go.
You've got to sort it yourself, but it's not very hard...
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Sprocket" inManagedObjectContext:managedObjectContext];
[request setEntity:entity];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"sortVariable" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[request setSortDescriptors:sortDescriptors];
NSError *error;
NSMutableArray *mutableFetchResults = [[managedObjectContext executeFetchRequest:request error:&error] mutableCopy];
self.sprocketArray = mutableFetchResults;
[sortDescriptors release];
[sortDescriptor release];
[mutableFetchResults release];
[request release];

Extract attributes from NSManagedObject array

NSFetchRequest *req = [NSFetchRequest init];
NSEntityDescription *descr = [NSEntityDescription entityForName:#"City" inManagedObjectContext:context];
[req setEntity:descr];
NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:#"streetName" ascending:YES];
[req setSortDescriptors:[NSArray arrayWithObject:sort]];
[sort release];
//fetch
NSError *error;
NSArray *result = [context executeFetchRequest:req error:&error];
//extract names
NSMutableArray *streets = [[NSMutableArray alloc] init];
for () {
??? = [array objectAtIndex:i];
[streets addObject:name];
}
I expected Core Data to be little more intuitive. I am new in it and I could use some help.
I fetched all objects(rows) from the entity (table) City. Now I have an array of objects. From the array I need to extract the attribute “streetName” to an array which will feed the picker. I figured I need to do it in the loop but I could not figure out the way to do it.
Please help.
I have a background with SQL but Core Data is still a big mystery to me. Is there any publication which would take a SQL statement and show comparable Core Data syntax?
Thanks.
It's very simple because of key-value coding:
NSArray *streets = [result valueForKey:#"streetName"];
i think core data changed quite a bit. here is how i do my fetch now
NSError *error;
NSFetchRequest *fr =[NSFetchRequest fetchRequestWithEntityName:#"Category"];
[fr setPredicate:[NSPredicate predicateWithFormat:#"name == \"myCategpry\""]];
NSArray *rs = [self.managedObjectContext executeFetchRequest:fr error:&error];
for (NSManagedObject *index in rs) {
NSLog(#"%#",[index primitiveValueForKey:#"name"]);
}

Fetch object by property in Core Data

In my iPhone project, I want to write a function that checks wether there's an object in my Core Data ManagedObjectContext with a given value for a certain property, say some_property.
If there's already an object with some_property == 12, I want the function to return the object, otherwise, I want to create the object, or at least return nil.
How would I do that?
The following snippet shows how to retrieve the objects matching a specific predicate. If there are no such objects, the snippet shows how to create a new object, save it and return it.
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"YourEntityName" inManagedObjectContext:managedObjectContext];
[request setEntity:entity];
// retrive the objects with a given value for a certain property
NSPredicate *predicate = [NSPredicate predicateWithFormat: #"property == %#", value];
[request setPredicate:predicate];
// Edit the sort key as appropriate.
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"yourSortKey" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[request setSortDescriptors:sortDescriptors];
// Edit the section name key path and cache name if appropriate.
// nil for section name key path means "no sections".
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:managedObjectContext sectionNameKeyPath:nil cacheName:#"Root"];
aFetchedResultsController.delegate = self;
NSError *error = nil;
NSArray *result = [managedObjectContext executeFetchRequest:request error:&error];
[request release];
[sortDescriptor release];
[sortDescriptors release];
if ((result != nil) && ([result count]) && (error == nil)){
return [NSMutableArray arrayWithArray:result];
}
else{
YourEntityName *object = (YourEntityName *) [NSEntityDescription insertNewObjectForEntityForName:#"YourEntityName" inManagedObjectContext:self.managedObjectContext];
// setup your object attributes, for instance set its name
object.name = #"name"
// save object
NSError *error;
if (![[self managedObjectContext] save:&error]) {
// Handle error
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
}
return object;
}
It's better if you don't do multiple fetching if you want to check for certain properties on the local data. Just do one fetch request using a pre-populated array and then iterate or filter the results.
This is a code snippet from Core Data Programming Guide "Implementing Find-or-Create Efficiently":
// get the names to parse in sorted order
NSArray *employeeIDs = [[listOfIDsAsString componentsSeparatedByString:#"\n"]
sortedArrayUsingSelector: #selector(compare:)];
// create the fetch request to get all Employees matching the IDs
NSFetchRequest *fetchRequest = [[[NSFetchRequest alloc] init] autorelease];
[fetchRequest setEntity:
[NSEntityDescription entityForName:#"Employee" inManagedObjectContext:aMOC]];
[fetchRequest setPredicate: [NSPredicate predicateWithFormat: #"(employeeID IN %#)", employeeIDs]];
// make sure the results are sorted as well
[fetchRequest setSortDescriptors: [NSArray arrayWithObject:
[[[NSSortDescriptor alloc] initWithKey: #"employeeID"
ascending:YES] autorelease]]];
// Execute the fetch
NSError *error;
NSArray *employeesMatchingNames = [aMOC
executeFetchRequest:fetchRequest error:&error];