Core data fetch request optimization with segment control - iphone

I have a segment control with 5 items, on selecting every item data is filter on some criteria and a different result is displayed. All the five choices in segment control use the same entity to fetch the data.
Currently i have a fetchresultcontroller and whenever there is a value change in the segment control i fetch data from the same entity with a different predicate and reload the table with new data.
I am looking to optimize this. Am I doing it the right way or what is the right way to do it?
Also what is the best way to change the sorting order between ascending and descending for an already fetched data.
Thanks in adv.

Can you post some code snippets?
If you already fetched a set of MangedObjcts you can reorder the NSArray with a Sort Descirptor: sortedArrayUsingDescriptors
- (NSArray *)sortedArrayUsingDescriptors:(NSArray *)sortDescriptors

If you're only fetching a small number of NSManagedObjects, there probably isn't much optimization to be done. CoreData and its corresponding classes (such as the FetchResultsController you are using, which is designed to work particularly well with UITableViews) do most of the heavy lifting for you.
In terms of the best way to change the sorting order between ascending and descending; this is determined in something like this:
NSSortDescriptor *sorter = [[NSSortDescriptor alloc] initWithKey:#"someEntityProperty" ascending:YES/NO];
If you set ascending to YES, you'll get your NSManagedObjects back sorted smallest to largest ascending) on the key you provide. If you set it to NO, you'll get them back largest to smallest (descending).

Related

Efficiently accessing array within array

I have a data type called Filter which has an NSMutableArray property which holds a bunch of FilterKey objects (different amount of keys for each filter). I have a bunch of these Filter objects stored in an NSMutableArray called Filters.
I have a UITableView for which each row is populated with data from one of the FilterKey objects. My question is, for a given row in the UITableView, how can I use the Filters array to find the right FilterKey (considering I've already put the Filters and Keys in order manually)?
Basically, I know I could just traverse through the Filters array and for each Filter object, traverse through all it's FilterKeys, but I'm wondering is there a better way to do this (ie better data structure, or something that would give me the right object faster?
Sorry if this is confusing if you have any questions please let me know in the comments.
Typically you would use sections and rows for this, where each section is a Filter and each row is a FilterKey.
It sounds like you just want to show the filter keys, and not have section headers for their filters (if I'm reading your post correctly). If you don't actually want headers, that's fine, just return 0 for tableView:heightForHeaderInSection: and nil for tableView:viewForHeaderInSection:.
All of this is really more for convenience than performance. It is unlikely that it will be much faster than running through the filters and adding up the counts of their keys. That kind of operation is extremely fast. But sections/rows maps your data better, so I'd probably use it anyway to keep the code simpler.
You can use NSMutableDictionary which is hash-mapped resulting in faster, easier, readable operations.
If you prefer arrays then there is no need to traverse to search for a specific value, you can use NSPredicate to filter your array.

filtering content for UITableView

I was looking at the TableSearch example code from Apple. It looks like that they have a NSArray for all the content, and a NSMutableArray for filtered content. And then if the filter is on, then they would show the NSMutableArray. If it is off, they would show the NSArray that has all the data.
1) I was wondering if this is a common implementation for filters since I haven't done much filtering before.
2) To add to that question, if I had a filter of four different categories, would I still use one NSMutableArray that shows the filtered content when the filter is on? Or do I create four different NSMutableArrays for each different type of filter, and then show that list depending on which filter is on.
Assuming that the common implementation is to have an NSArray for the list, I'm getting confused if creating the arrays of filtered list up front is expensive if I were to do four different NSMutableArrays, or if depending on the click from the user of what filter option they select, should I create the NSMutableArray on the fly, and then reload the [tableView reloadData];
Thanks.
I don't have that sample app in front of me, but you typically would filter using a predicate, so it would be helpful for you to review the docs on NSPredicate.
So when you want to change the filter, you do so by changing the predicate. You don't have to create all filtered results. You only create the one you need at any given moment.
With arrays, you can filter using code like that shown in this example. The key lines are
NSPredicate *predicate;
predicate = [NSPredicate predicateWithFormat:#"length == 9"];
NSArray *myArray2 = [myArray filteredArrayUsingPredicate:predicate];
Filtering is not always done with arrays. It can be done with NSFetchedResultsControllers if using Core Data. Predicates are used there also, in very much the same way. Predicates can be used for other things, too, including regular expression filtering. It's worth looking at, if you aren't familiar with it.
It really depends. If your underlying data is in Core Data, use NSFetchedResultsController and give it NSPredicates. If you have an array of data, it may be easiest to traverse it and create another array of data.
In general, the filter itself is not likely to be as expensive as the overall drawing process (which includes instantiating or recycling table cells). You can do what's easy and profile with Instruments.
Keeping four different arrays is normally not a good idea in terms of memory, which is a scarce resource.
No matter what though, reloadData is going to be involved. (Depending on OS version, perhaps — see the NSFetchedResultsController docs.)

Fast Core Data Importing Question

I use Core Data on my iPhone app to store about 1000 objects. Each object is assigned one of 5 different groups. Over time, the objects change groups, and within each group, the order of the objects in those groups change and is stored in an array.
I then take this array and use the attribute 'name' to store it to a plist. When I relaunch the app, my AppDelegate scans each object in the database, then compares it to the 'name' attribute in my plist. When this completes, I have 5 arrays of core data objects, sorted by group, each in the original saved order. The end result is good. The time it takes to complete this task is not.
I want to make this faster. If I could save the actual core data object array to my plist, I would do that. But I can only save attributes to it.
Apple has documentation on Implementing Find-or-Create Efficiently, specifically, this code:
// 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]]];NSError *error = nil;
NSArray *employeesMatchingNames = [aMOC
executeFetchRequest:fetchRequest error:&error];
However, the fetchRequest sets a SortDescriptor and sorts using an 'employeeID' key. I tried this. I have an ID key attribute and can save the array index position when an object is added to a group. However, because objects are being added and removed from each group, the index of the object is constantly changing. So after each change, I would have to rescan each group and reset the index. To me, this would just move my speed problem to another part of my program.
My questions are: is there a better way to save the index position of objects in an array? or is there a different place I can store an array of core data objects? If I did the latter, though, because my app is already in the appStore, my understanding is if I add attributes or tables to the database, this can cause problems when a user upgrades.
I hope I explained this well. Any help would be appreciated, thanks.
You are making this way to complicated. The problem you want to solve i.e. ordering objects based on an attribute value, is a very common need e.g. every single tableview requires this type of ordering.
Ordering is why you can provide one or more sort descriptors to a fetch. In this case you want to order on an employeeID attribute so you just provide a sort that on that key. The array returned by the fetch will be sorted on that attribute.
When you add or remove objects, you just rerun the fetch again either directly or by using notifications. If you use a NSFetchedResultsController, this is all handled for you automatically.
If you need some kind of arbitrary ordering, say a user's list of favorites, then you need to model that arbitrary order directly in the data model. There are various means of doing so depending on the specific type of ordering needed.
Here is a good rule of thumb when designing with Core Data: If you have any other data structure to hold or order managed objects beyond the arrays returned by fetches, you've done something wrong.
Used properly, Core Data can managed the entire data model without any external structures. If you find yourself welding extra data structures onto Core Data, then you've missed something in your data model design.
On the iPhone there is no way to maintain an ordered collection other than storing an index. That property is an integer, correct?
If you add attributes what happens is you need to set up the persistantStoreCoordinator in a way that it attempts an auto-migration. If you never created another model version before though, this will be tricky. You should try that and see if you can get it to work as you are probably going to have to do it someday...
My feeling on CoreData is that the migration is tricky enough you should always keep any user generated data to the side in something like a plist also, that you can re-build a data base from. It sounds like you may already be doing that? If so, you can check when you create the persistantStoreCoordinator if it has failed, and if so then erase the database that is there, create the persistantStoreCoordinator again, and populate that database from your plists.

Core Data: How to do a second fetch limited to the results of an earlier fetch?

This seems like it may be a trivial question, but I am new to Core Data and to databases.
In my application, I perform a fetch and display the results. Then, based on user input, I need to cull those results down. That is, I need to do a new search on only the results of the first fetch, but based on an entirely different parameter. (Sometimes, the second fetch will be based on an attribute, other times on a to-many relationship.) What is the optimal way to do this?
I have figured out two options to do this, but neither seems very good:
In the first fetch, prefetch all the data needed for the second fetch. Then, don't do a second fetch, but just iterate through the array of results of the first fetch, looking for matches to the new conditions of the second fetch. This method has the disadvantage that I have to trudge thru the array and don't take advantage of performance benefits of Core Data.
For the second fetch, disregard the first and do a brand new fetch with a compound predicate composed of the conditions for the first fetch and those for the second. This has the disadvantage that Core Data must look thru the entire database again to do the same search it already did.
Is there a way, in a second fetch, to tell Core Data to search only thru the entity objects returned in an earlier fetch?
You've pretty much got it figured out.
For the first option, it's pretty easy. You have your array of objects, and you can just do -[NSArray filteredArrayUsingPredicate:] or -[NSMutableArray filterUsingPredicate:] to reduce the array according to your needs. You don't need to actually iterate through the array yourself; just use a predicate like you would with a fetch request.
For the second option, that's also pretty easy. You take the predicate from your first request and AND it with your new predicate:
NSPredicate *original = ...;
NSPredicate *newCondition = ...;
NSPredicate *newFilter = [NSCompoundPredicate andPredicateWithSubpredicates:[NSArray arrayWithObjects:original, newCondition, nil]];
Personally, I usually find the first option to be simpler overall.

Selection of random entries from a Core Data Store

Is there a way to select a fixed number of random entries from a Core Data store? I am just getting started with Core Data and have been stuck on this problem for quite some time.
As a last resort, I could query a large selection of entries into memory and then randomly select a fixed number.
Also, is there a way to specify custom SQL statements to be executed on the Core Data store? I realize that this would be highly unlikely since the underlying implementation of the store could be an XML file as well.
Mmm... maybe
[[[managedObjectsContext registeredObjects] allObjects] objectsAtIndex:r]
where r is random int between 0 and the number of objects minus one? Not efficient at all but quick and easy.
EDIT: if you want to pick the random object between a selection of your objects then create a fetch request that describes your object selection and do the same as above with the query results:
[[[managedObjectsContext executeFetchRequest:request error:&error] objectAtIndex:r]
With regards to your second question, that is one of the points of Core Data, to abstract away the underlying data store. Using NSPredicate and NSExpressions to build a fetch request, or storing one in the data model is the only way to query the store.
Not sure, but I think Core Data puts data into NSSets for you. So you might be able to use -[NSSet anyObject]. I haven't tested that or anything.