Show only unique results within core data NSFetchResultController - ios

I wish to show only unique results from the core data fetch request. Currently from research I have seen it is possible to achieve this from using NSDictionaryResultType but I have struggled to get it working.
I did try using the following but couldn't intergrate it correctly into my class. I was not 100% sure what to put after NSArray *distincResults as it came up unused variable:
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Routines" inManagedObjectContext:managedObjectContext];
request.entity = entity;
request.propertiesToFetch = [NSArray arrayWithObject:[[entity propertiesByName] objectForKey:#"routinename"]];
request.returnsDistinctResults = YES;
request.resultType = NSDictionaryResultType;
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"routinename" ascending:YES];
[request setSortDescriptors:[NSArray arrayWithObject:sortDescriptor]];
NSError *error = nil;
NSArray *distincResults = [managedObjectContext executeFetchRequest:request error:&error];
// Use the results
Any suggestions?

If you use NSDictionaryResultType, you cannot use the FRC delegate to watch for changes. If that is OK, you can go down this route.
Once you have an array of dictionaries (distinctResults) make that the data array of your table view. So, for example, in cellForRowAtIndexPath or configureCell, use
cell.textLabel.text = distinctResults[indexPath.row][#"routinename"];
This is the short form of
cell.textLabel.text = [[distinctResults objectAtIndex:indexPath.row]
objectForKey:#"routinename"];

Related

Sort Array of FetchedObjects From Core Data by Number of Reviews in Objective-C

I would like to sort an array of Reviews fetched from Core Data by the number of times a given book is reviewed. The Entity is reviews and the review entity has an attribute bookid. So for the following table:
reviewid|bookid|review
1|1|This is a great novel...
2|1|Wonderful novel
3|2|Ok biography.
4|3|Horrible romance
I would like to return an array that has bookid=1 at the top as it has two reviews whereas the others have one.
How can I do this using NSSortDescriptor?
I believe, you can do it to the array after the fetch using the following but it seems that it should be possible to do it directly in the core data results
NSEntityDescription * entity = [NSEntityDescription entityForName:#"Parent" inManagedObjectContext:self.managedObjectContext];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:reviews];
NSError *error;
NSArray *results = [self.managedObjectContext executeFetchRequest:request error:&error];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"bookid.#count" ascending:NO];
NSArray *descriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
NSArray *sortedArray = [results sortedArrayUsingDescriptors:descriptors];
You can set the NSFetchRequest's sort descriptor.
request.sortDescriptors = #[sortDescriptor]
Read the documentation for more info.
https://developer.apple.com/documentation/coredata/nsfetchrequest/1506262-sortdescriptors

Return most recent NSManagedObjects only from NSFetchRequest

I am using an NSFetchedResultsController and need to return distinct objects based on their latest dates and name. Each date is stored as an NSDate.
Example:
Object1
name:Object1
date:01/01/2001
Object2
name:Object
date:01/02/2001
Object3
name:OtherObject
date:01/10/2001
Object4
name:OtherObject
date:02/01/2001
Expected results (Return only the latest date objects when a duplicate is found):
name:Object
date:01/02/2001
name:OtherObject
date:02/01/2001
// Below returns everything sorted correctly although I only need the latest of each match based on the date.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:#"Object" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
// Create the sort descriptors array.
NSSortDescriptor *name = [[NSSortDescriptor alloc] initWithKey:#"name" ascending:YES];
NSSortDescriptor *date = [[NSSortDescriptor alloc] initWithKey:#"date" ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:name, date, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
EDIT
As of now I am fetching all of the objects into an array, for each object in array1 I cycle thru and add / compare it to the latest matching objects in array2 with the same name and remove the oldest date entry from array2 then I'm left with unique latest dates in array2. I was hoping there was a less cumbersome way to do this with a single fetchrequest / subquery.
Core Data doesn't automatically track insertion order. When you get an fetched object back, its entities will be in any random order.
My immediate thoughts on this are that you need another attribute to track the insertion time and reorder/sort by this attribute.
So, your attribute would be need to be a date time with seconds. You would call this:
NSDate *now = [[NSDate alloc] init];
and store it in the attribute. When you fetch the object, sort by the key of this attribute.
Try this...
Create an NSSet of the attribute name to return a unique set of data.
Fetch the latest attribute date for each attribute name.
For example:
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Object"
inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
NSArray *objects = [self.managedObjectContext executeFetchRequest:fetchRequest];
NSMutableArray *arrayObjectNames = [[NSMutableArray alloc] init];
for (Object *object in objects) {
[arrayObjectNames addObject:object.name];
}
NSSet *setObjectNames = [NSSet setWithArray:arrayObjectNames];
NSString *key = #"name";
NSMutableDictionary *dictNamesDates = [NSMutableDictionary dictionary];
for (NSString *objectName in setObjectNames) {
NSFetchRequest *fetchName = [[NSFetchRequest alloc] initWithEntityName:#"Object"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"%K == %#", key, objectName];
[fetchName setPredicate:predicate];
NSSortDescriptor *sortDate = [[NSSortDescriptor alloc] initWithKey:#"date" ascending:YES];
[fetchName setSortDescriptors:#[sortDate]];
Object *targetObject = [[self.managedObjectContext executeFetchRequest:fetchName] lastObject];
if (targetObject) [dictNamesDates setObject:targetObject.date forKey:objectName];
//OR as an alternative but achieving the same outcome...
if (targetObject) [dictNamesDates setValue:targetObject.date forKey:targetObject.name];
}
This should create an NSMutableDictionary that contains each attribute name as the key and the corresponding latest attribute date as the value.
Does this help?

NSSortDescriptor using the minimum value of a date in a related object

I have a Core Data entity, MediaAsset. I have another called UploadAttempt which tracks all of the attempts we've made to upload that MediaAsset to our central server. I would like to fetch MediaAssets from the datastore ordered by the first UploadAttempt.
MediaAsset has 3 UploadAttempts, A, B, and C. UploadAttempt has an attemptDate attribute and I would like to find the earliest UploadAttempt for each MediaAsset and use that as the sorting.
I would like to create an NSSortDescriptor something along the lines of:
order by MediaAsset.uploadAttempts.attemptDate where attemptDate is the earliest attemptDate for that particular MediaAsset.
Does that make sense? Any help is appreciated.
(note: assume I cannot add an additional attribute to the MediaAsset. This is already existing data and I can't alter it.)
If I understand your question correctly, the following should work. If not, please ping me again, we will understand and correct it together.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entityDesc = [NSEntityDescription entityForName:#"UploadAttempt"
inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entityDesc];
NSError *error = nil;
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"date"
ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
NSArray *dateSortedAttempts = [self.managedObjectContext executeFetchRequest:fetchRequest
error:&error];
NSSet* mediaAssets= [NSSet setWithArray:[items valueForKeyPath:#"#distinctUnionOfObjects.MediaAsset"]];

how to sort a view-based tableview

I can't manage to sort a view-based tableview. I use an arrayController that control an entity of core data.
I tried to select a column and in the attribute inspector I used as a sort key the attributes name relative to that column and compare: as a selector... when I build and run I click on the header and now display the arrow that change every click, but the nothing happens with the rows. no sorting.
How can I fix it?
I think I'm missing something easy, but I can't get over it.
You may want to consider using an NSFetchedResultsController. From the Apple documentation for NSFetchedResultsController,
You use a fetched results controller to efficiently manage the results returned from a Core Data fetch request to provide data for a UITableView object.
The code looks like this where the line that provides the sort descriptor starts with NSSortDescriptor.
- (NSFetchedResultsController *)fetchedResultsController
{
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"YourEntityName"
inManagedObjectContext:yourManagedObjectContext];
[fetchRequest setEntity:entity];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"YourSortKey"
ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
NSError *error = nil;
NSArray *fetchedObjects = [yourManagedObjectContext executeFetchRequest:fetchRequest
error:&error];
if (fetchedObjects == nil) {
// Handle the error
}
yourFetchedResultsController = [[NSFetchedResultsController alloc]
initWithFetchRequest:fetchRequest
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:nil
cacheName:nil];
return yourFetchedResultsController;
}

How to get CoreData fetch results into (multidimensional) arrays?

I want to get data from multiple rows in coredata into a multidimensional array so I can loop through them to create events in a calendar. However, it doesn't seem possible or advisable from an objects standpoint to have a true multidimensional array, so I've created one NSMutableArray per column of data I want to use for the event attributes (title, note, time of day).
But how do I assign all the values for each of the columns into its own NSMutableArray? Or should I use a NSDictionary to hold the values?
Here's my fetch from CoreData which is pretty standard:
MyAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [appDelegate managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entityDescription = [NSEntityDescription entityForName:#"My_List"
inManagedObjectContext:context];
[fetchRequest setEntity:entityDescription];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"my_list_name" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
[fetchRequest setFetchBatchSize:20];
[sortDescriptors release];
[sortDescriptor release];
NSFetchedResultsController *fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:context
sectionNameKeyPath:nil
cacheName:#"my_list.cache"];
fetchedResultsController.delegate = self;
NSError *error;
BOOL success = [fetchedResultsController performFetch:&error];
if (!success) {
//Handle the error
}
self.resultsController = fetchedResultsController;
NSArray *fetchedObjects = [fetchedResultsController fetchedObjects];
Here I'm going speculate that I should loop through my NSManagedObject for each of the arrays but I'm not sure how.
for (NSManagedObject *list in context)
{
[ reminderTitleMutableArray addObject:my_list_List.my_list_name ];
[ reminderTitleMutableArray addObject:my_list_List.my_list_description ];
[ reminderTitleMutableArray addObject:my_list_List.my_list_tminus ];
}
Is this the right way?
Thanks
I strongly advise to not pursue this design pattern. By creating multiple or multidimensional arrays, you are cluttering your memory with data that is anyway stored in the core data persistent store. Memory problems are probable.
It would be much better to use some kind of datasource scheme, which I am sure you know from UITableViews, and retrieve the data for each date in your calendar as you need it. With a fetchedResultsController this is quite easy to achieve with NSIndexPaths or some other scheme suitable for your calendar.

Resources