In raw SQL it would be simple to sort with all bobs coming first.
How can I do this using a NSFetchedResultsController and NSSortDescriptor?
I think that you cannot do that with NSSortDescriptor, because this is for key-value sorting and does not support expressions.
However, it could work with the help of NSFetchRequest. Set the result type of the fetch request to dictionary
fetchRequest.resultType = NSDictionaryResultType;
Then you can set the properties to fetch to the properties you want plus add a expression for computed properties:
NSExpression *expression = [NSExpression expressionWithFormat:#"name=='bob'"];
NSExpressionDescription *expressionDescription = [NSExpressionDescription new];
expressionDescription.name = "isCalledBob";
expressionDescription.expression = expression;
expressionDescription.resultType = NSBooleanAttributeType;
[fetchRequest propertiesToFetch:#[…, expressionDescription];
Then you can use NSSortDescriptor on the key isCalledBob.
With this you get dictionaries instead of managed objects.
Typed in Safari, did not test it, my kid wakes up in some minutes.
The solution I found to this is kind of problem is to just add an extra field to help with the FRC. ie isBob which gets updated whenever the name is set.
Related
I have a fairly large set of Core Data entities with a date property. I'm creating a report from the entities and need to find the report date range. There are ~1000 records within the report.
I'm thinking of getting the fetched objects array from my NSFetchedResultsController and sorting the array using date sort descriptor. Then getting the first and last object of that array to determine the date range. I seem to recall that date operations are expensive and am not sure if sorting an array that way, getting a 1000 element array back is a good idea.
Is there some other core data or predicate trick that I can use to query my core data stack to find an object with the minimum or maximum date?
Here's the code that I'm currently using, but am wandering if there's something faster and more efficient:
NSSortDescriptor* sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"date" ascending:YES];
NSArray* sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
NSArray* sortedDateArray = [NSMutableArray arrayWithArray:[fetchedResultsController.fetchedObjects sortedArrayUsingDescriptors:sortDescriptors]] ;
NSManagedObject* firstDateObject = [sortedDateArray objectAtIndex:0];
NSManagedObject* lastDateObject = [sortedDateArray lastObject];
No need to use a FetchedResultsController. Just use a simple FetchRequest, and take advantage of the aggregate functionality.
The following document actually has an example of fetching based on the minimum date attribute of an entity.
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CoreData/Articles/cdFetching.html#//apple_ref/doc/uid/TP40002484-SW6
I have a CoreData entity:
A {
prop1
prop2
}
I am in need of building a simple fetch, for retrieving all prop1 of NSManagedObject that has prop2 of a given value.
This is easily explained with a sql statement.
SELECT A.prop1 WHERE A.prop2=<value>
That's it, just all the values with no distinct clause. So far, I found that the only way to achieve this is by retrieving all the entities with a NSPredicate:
NSPredicate *allProps = [NSPredicate predicateWithFormat:#"prop1 == %#",prop];
The thing is that I only want an NSArray of prop2 rather than the full set of entities to iterate. I was hoping that NSExpression could give me the ability to accomplish that, but I cannot find a way.
Do you have any suggestion ?
I don't quite get the last part of your question. I suppose what you want can be acheived by the following code
[request setEntity:[NSEntityDescription entityForName:#"A" inManagedObjectContext:context]];
[request setPropertiesToFetch:[NSArray arrayWithObject:#"prop1"]];
[request setPredicate:[NSPredicate predicateWithFormat:#"prop2==%#",prop]];
[request setResultType:NSDictionaryResultType];
Of course, you would have to set your sort descriptors and all, but this should do the trick.
I'd like to implement a dynamic property on a Core Data model but am unclear as to whether this is supported, and if so... how?
The idea is to have a model with a set number of non-dynamic fields, such as created_at, updated_at, start_at, end_at, etc. These would be dates. I'd then like to include a dynamic property called "is_archive" which would perform a basic check against the "end_at" property and return True or False.
I know that I can update the Model to add a custom property, but am unclear as to how I could implement this so that I can include "is_archive" in an NSSortDescriptor.
Right now, I have:
NSFetchRequest *request = [[NSFetchRequest init] alloc];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"event" inManagedObjectContext:managedObjectContext];
[request setEntity:entity];
NSSortDescriptor *sort1 = [[NSSortDescriptor alloc] initWithKey:#"start_at" ascending:YES];
[request setSortDescriptors:[NSArray arrayWithObjects:sort1,nil]];
What I'd like to do is add:
NSSortDescriptor *sort2 = [[NSSortDescriptor alloc] initWithKey:#"is_archive" ascending:NO];
In SQL, I would normally do this with a CASE statement, such as:
select *
from event e
order by case when e.end_at > curdate() then 1 else 0 end desc, e.end_at asc;
So I suppose the question is:
How can I do a "CASE WHEN x > y THEN true ELSE false END" with a Core Data NSSortDescriptor, or the equivalent.
David,
It is difficult, if not impossible, to make a fetch request using dynamic properties. In Core Data, one typically over fetches an entity and then refines the resultant array using predicates, such as with -filteredArrayUsingPredicate:. While at first glance this appears to be wasteful, it is relatively memory efficient and fast.
Andrew
use NSPredicate
check out this link http://cocoawithlove.com/2008/03/core-data-one-line-fetch.html
---edit
I can't tell you how to do this in clear and good code. But you can create two arrays using predicate - one with array of is_archived set to YES and the other set to NO, apply sort descriptors to both and concatenate them. I know that's pretty dirty...
I'm having the same problem as this question, but the answer of #davedelong is not working for me.
When following the Apple Example, for fetching the smallest date in a set of object I get the following error
-[NSDate count]: unrecognized selector sent to instance
My understanding is that NSExpression's max: only support NSArrays. So I need an other solution.
#davedelong suggested using an ascending NSSortDescriptor, and so I did :
NSFetchRequest* fetchRequest = [[NSFetchRequest alloc] init];
fetchRequest.entity = [NSEntityDescription entityForName:NSStringFromClass([GCSession class])
inManagedObjectContext:self.objectContext];
fetchRequest.fetchLimit = 1;
NSSortDescriptor* sort = [[NSSortDescriptor alloc] initWithKey:#"startDate" ascending:YES];
fetchRequest.sortDescriptors = [NSArray arrayWithObject:sort];
GCSession* session = [[self.objectContext executeFetchRequest:fetchRequest error:nil] lastObject];
return session.startDate;
The problem here is that the session object returned from the fetch doesn't seems to be the one with the smallest startDate. In my tests, it seems that it even returned the newest date but it doesn't seems consistent.
I could also fetch every GCSession object and sort them but that seems way overkill, especially that GCSession will be augmenting in number when the users will use the application.
Edit : A test project that demonstrate the bug in Apple's example code.
A sort only comes into play after you've fetch the objects. You've set a fetch limit of one and no predicates which tells the fetch "go grab any random single GCSession object". One the fetch has an array 1 element long, it then sorts it, which is useless.
If you want to use a sort to find a min or max, you have to fetch all the objects and then sort them. Removing the line:
fetchRequest.fetchLimit = 1;
… should allow the code to work.
However, you should be able to fetch min and max values with expressions. It's kind of a basic operation.
Edit: Look at the comments for more detail, but Apple's example can work if you change the backing store to SQL instead of XML
trying the solution here:
https://stackoverflow.com/questions/1741093?tab=newest#tab-top
I'm using a transient property and the category solution and it seems to be working right up until the index char starts to wrap around to the A's again, not sure why it's doing that, just logging what the category/transient getter is returning for uppercaseFirstLetterOfName.
I'm sorting using the name property and then setting sectionNameKeyPath on the fetchRequest to uppercaseFirstLetterOfName.
The full error is:
NSFetchedResultsController ERROR: The fetched object at index 248 has an out of order section name 'Y. Objects must be sorted by section name'
Any ideas where I might have gone wrong or how to even track down the problem?
I found the problem.
Because the sort was producing caps and lower case dups I suppose but the indextitles weren't I got that out of order index/section name:
just added this to the fetch
selector:#selector(caseInsensitiveCompare:)
so it is now:
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"name" ascending:YES selector:#selector(caseInsensitiveCompare:)];
and works
cracker jack!