Return most recent NSManagedObjects only from NSFetchRequest - ios

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?

Related

to-many relation as sectionNameKeyPath

I want to show Project such that it shows all the projects on the basis of sharedToUsers basis. Means section name with User.user_id and it should shows all the shared project with that user. but i am not able to set section value for NSFetchedResultsController properly because it is a too many relation and it is crashing on sectionNameKeyPath .
Any Help
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *theParent = [NSEntityDescription entityForName:#"Project" inManagedObjectContext:_managedObjectContext];
//the name key exsit in parent entity of project.
NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:#"name" ascending:YES];
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:sort]];
[fetchRequest setFetchBatchSize:10];
[fetchRequest setEntity:theParent];
[fetchRequest setPredicate:_predicate];
NSFetchedResultsController *theFetchedResultsController =
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:_managedObjectContext sectionNameKeyPath:#"ANY sharedToUsers.user_id"
cacheName:[self getCacheNameForNSFetchedResultsController]];
if you have a senario like i have then when ever you change data in the core data then try donot use to add or update relation ship items using object of parent or category object. if you had to link some thing with category then try to link from child object.
donot use this
[category addRelationShipItem:child];
use this
`[child addToCategory:category];`
when you use this then it will not triger update call in NSFetchResultController call.
Happy coding.
Hi If you want fetch the Project shared by a user you need this predicate:
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *theParent = [NSEntityDescription entityForName:#"Project" inManagedObjectContext:_managedObjectContext];
NSString *userId; // If you user_id is a NSNumber change you to it.
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"ANY sharedToUsers.user_id = %#",userId];
[fetchRequest setPredicate:predicate];
Last things you are using a NSSort: name , but name it's not a Project property.

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"]];

Show only unique results within core data NSFetchResultController

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"];

NSSortDescriptor with fetched property core data

I have an Agency entity that has a fetched property city. I'm performing a fetch request and I want my results to be sorted by agency's name and by city's name.
This is the part of old code. As you can see there was a relationship to the entity City and I could setup my sort descriptor easily. But now I've replaced relationship by fetched property.
Help me please, how I need to modify this code?
self.context = //...
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *callEntity = [NSEntityDescription entityForName:#"Agency" inManagedObjectContext:self.context];
[fetchRequest setEntity:callEntity];
NSSortDescriptor *sortByCityNameDescriptor = [[NSSortDescriptor alloc] initWithKey:#"city.name" ascending:YES selector:#selector(caseInsensitiveCompare:)];
NSSortDescriptor *sortByNameDescriptor = [[NSSortDescriptor alloc] initWithKey:#"name" ascending:YES selector:#selector(caseInsensitiveCompare:)];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortByCityNameDescriptor, sortByNameDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
//performing a fetch

divide fetched results NSDate into sections for each single day use core data

I know that fetched results controller have the section name key path can divide fetched results into sections.
But how could I divide NSDate into sections for each day or each month?
Or any other ways to solve this problem?
Thanks.
What you need to do is to create a transient property on your data object, and then sort your fetched results accordingly. For a TVGuide I've worked on, I needed to sort results by airDay, and ended up sorting the events by startDate, and using the transient property for section key name path:
In Event.m:
-(NSString*) airDay
{
NSDateFormatter *dayFormatter=[[NSDateFormatter alloc] init];
[dayFormatter setLocale:[NSLocale currentLocale]];
[dayFormatter setDateStyle: NSDateFormatterMediumStyle];
[dayFormatter setDoesRelativeDateFormatting: YES];
return [dayFormatter stringFromDate:self.startDate];
}
The matching fetchrequest
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Event" inManagedObjectContext:[Database db].managedObjectContext];
[fetchRequest setEntity:entity];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:
[NSSortDescriptor sortDescriptorWithKey:#"startDate"
ascending:YES],
nil];
[fetchRequest setSortDescriptors:sortDescriptors];
NSFetchedResultsController *frc = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:[Database db].managedObjectContext
sectionNameKeyPath:#"airDay"
cacheName:#"SearchEvents"];
Please refer to the Apple sample code of "DateSectionTitles", you can search this in the Xcode help.
it helps a lot!!

Resources