iOS NSFetchedResultsController: sort sections by >=2 properties - ios

I use NSFetchedResultsController to fetch from database with Core Data. And I have an Entity with 2 properties, prop1 and prop2 of NSString.
How would I sort sections not only by one of the properties but both?
Now it is:
Title1ForProp1/Title2ForProp2 (prop1==1 prop2==2)
Title1ForProp1/Title1ForProp2 (prop1==1 prop2==1)
Title2ForProp1/Title1ForProp2 (prop1==2 prop2==1)
I need:
Title1ForProp1/Title1ForProp2 (prop1==1 prop2==1)
Title1ForProp1/Title2ForProp2 (prop1==1 prop2==2)
Title2ForProp1/Title1ForProp2 (prop1==2 prop2==1)

When you create the fetch request for the NSFC you create the sort descriptor and give the request an array.
You can put as many sort descriptors in the array as you like.
Just create a sort descriptor for each field you want to sort by.
I can remember which order you have to put them into the array though.
OK, so code wise...
NSSortDescriptor *sd1 = [[NSSortDescriptor alloc] initWithKey:#"prop1" ascending:YES];
NSSortDescriptor *sd2 = [[NSSortDescriptor alloc] initWithKey:#"prop2" ascending:YES];
[fetchRequest setSortDescriptors:#[sd1, sd2]];
This is all you have to do.
The NSFC will only split them into sections if you give it a sectionNameKeyPath. If you don't want any sections then make the sectionNameKeyPath nil.

Fogmeister's sort descriptors array is appropriate (sort on prop1 then prop2) but if you provide prop1 in your sectionNameKeyPath your sections would be broken up only by prop1. Within each section, the items would be sorted by both prop1 and prop2.
If this is not what you want and you need to additionally group your results into sections by both prop1 and prop2, you probably want to create a transient property that concatenates both prop1 and prop2 and provide that transient property as your sectionNameKeyPath. This provides not just the title for the section but also determines how results are grouped into sections.
Take a look at this question for how you might create a transient property for your section names:
NSFetchedResultsController with sections created by first letter of a string

Related

Fetching latest value from core data and assigning it to a label. Swift

I've only been coding for 2 weeks now and I am trying to build a 'weights logger app'. I am saving the weights under the entity - "Weights" with attributes for "benchPress", "deadLift" etc.
I figured out how to save values. So when the user clicks save, it saves the recorded value to the array.
But I don't know how to fetch the 'latest' value and set it equal to a UILabel.
Image showing the entity
The code I have so far to fetch the result
Any help would really be appreciated. I've been stuck on this for a while now. Thanks!
Core Data doesn't have any concept of "latest". It doesn't track when new data has been saved. If you need something like the most recent entry, you need to add your own field-- a date, or an integer index, or something that would indicate the correct instance to fetch.
If you added a date field named date, you'd get the entry with the most recent date using a fetch request something like this:
NSFetchRequest *fetch = [NSFetchRequest fetchRequestWithEntityName:#"Weights"];
NSSortDescriptor *dateSort = [NSSortDescriptor sortDescriptorWithKey:#"date" ascending:NO];
fetch.sortDescriptors = #[ dateSort ];
fetch.fetchLimit = 1;
The result would be an array containing at most one object, which would have the most recently saved date.

Multiple sort on Array for UITableView

I have an array of categories returned from a parse.com query (sorted ascending) that are listed in a TableView.
I want to place a category called "Everything" at the top of the list and then sort the rest of the list ASC. I'm thinking I should use NSDescriptor, NSComparator or NSPredicate but wanted to get some feedback on the best route.
Another angle I thought might work is to extract "Everything" out of the array and put it in a string and then use 2 custom cells and put "Everything" on top.
Any advice on which option would be best? Or is there another route I missed?
I solved this by copying the results Array into a mutable Array and then sorted the Array.
NSMutableArray *sort = [[NSMutableArray alloc] initWithArray:self.objects];
id tmp=[sort objectAtIndex:6];
[sort removeObjectAtIndex:6];
[sort insertObject:tmp atIndex:0];

CoreData fetch with NSSortDescriptor by count of to-many relationship

I have 2 CoreData entities (Person, Pet) with the following relationship:
A Person can have multiple Pets. So, there is a To-Many relationship from Person->Pet.
I want to fetch all Persons, sorted by the count of Pets that they have where the person with the most Pets is first, and the person with the fewest Pets is last.
When fetching a list of Persons, I tried using an NSSortDescriptor like the following:
[NSSortDescriptor sortDescriptorWithKey:#"pets.count" ascending:NO]
I thought that the "pets" property on a "person" would allow me to use the count property of NSSet to sort. Alas, CoreData complains with this error:
Fetch exception to-many key not allowed here
Is this a completely wrong approach? How am I supposed to do something like this with a CoreData fetch?
Thanks in advance for your help.
Try using collection operators:
[NSSortDescriptor sortDescriptorWithKey:#"pets.#count" ascending:NO]
This can't be applied directly though (to a fetch request), you need to execute the fetch and then run the sort on the result array (sortedArrayUsingDescriptors:).
If you want to sort while fetching, you would need to add a (non-transient) attribute to the entity which holds the count number.
Change the format of your key to #"pets.#count"
Take a look at this answer https://stackoverflow.com/a/20317087/2567126
Basically you create a persistent property on the NSManagedObject and update the property count any time a child object is added or removed from the relationship.
I also need to do the same thing but in Swift. I just to follow:
Fetch the request from CoreData without 'count' key-value.
I performed sort (high order function) on the fetched result.
allCategory.sort { (category1, category2) -> Bool in
return category1.tasks?.count > category2.tasks?.count
}
This is working for me.

NSFetchedResultController sort table view sections by multiple keys

Problem:
in my CoreData model, i have two entities called List and Item, they have one-to-many relationship. There are 2 types of List in my current design, system list and user list. System list is created by system during the very first launch and user list will be created by users anytime.
In my TVC, i would like to present all the items grouped by their associated lists(use list name as section title), in addition, i want all the system lists always placed on top of the user lists.
For instance, consider i have all the instances of list in DB as below:
System list: Inbox
User list: FruitList, ShoppingList
The expected result will be:
Inbox
InboxItem1
InboxItem2
FruitList
FruitListItem1
ShoppingList
Apple
Orange
The Approach:
With FRC, i can a achieve the first goal by setting sectionNameKeyPath as "list_name" to the FRC. But i have problem with fixing position the system list (Inbox in this case, it should always at index 0). However, the problem comes as user input their list with name start with "A" - "J" and all those lists will be placed on top of inbox, and i did try to fix it by adding "list_type" as secondary sort key, but it doesn't work.
NSFetchRequest *requst = [[NSFetchRequest alloc]initWithEntityName:ENTITY_ITEM];
NSArray *sortDescriptors = #[
[NSSortDescriptor sortDescriptorWithKey:#"belongsToList.list_name" ascending:YES],
[NSSortDescriptor sortDescriptorWithKey:#"belongsToList.list_type" ascending:NO]];
requst.sortDescriptors = sortDescriptors;
requst.predicate = [self predicateForListType:type selectedList:list];
requst.fetchBatchSize = 20;
_fetchedResultsController = [[NSFetchedResultsController alloc]initWithFetchRequest:requst managedObjectContext:self.managedObjectContext sectionNameKeyPath:#"belongsToList.list_name" cacheName:nil];
Temporary solution:
The temp solution i can think of to solve this problem is to add prefix string to the list name and store them as another attribute from DB and use it as sectionNameKeyPath.
e.g.
"0-" - for system lists ("Inbox" -> "0-Inbox")
"1-" - for user list ("ShoppingList" -> "1-ShoppingList")
and remove the prefix in -tableView:titleForHeaderInSection: . But this solution creates overhead and i think there should be much better solution out there since it is a common problem that all developers will face while they are dealing with FRC and TBV.
Any input??!!!
You can add a transient attribute like sectionIdentifier to the List entity and sort like this: use
[NSSortDescriptor sortDescriptorWithKey:#"list.sectionIdentifier" ascending:YES]
as the first sort descriptor.
The sectionIdentifier could be anything which sorts the way you intend ("System" first) and from which you can reconstruct the proper section name to return in titleForHeaderInSection.

insertNewObjectForEntityForName at first position in the table not last

How to insert the record with coredata at first position in table? currently if i add insertNewObjectForEntityForName it adds record to last.
Well, as far as i know this is not possible. However one thing you could do is manipulate how the objects are retrieved from the DB. For example, for such requirements it is often useful to have an NSDate property in your model class. While inserting objects into the DB, pass [NSDate date] for this property. And while pulling out objects from the DB, write a sorting algorithm which returns the records sorted by the Date of insertion
In your model class' header file
Header.h
//Declare this property
NSDate *dateOfInsertion
//This method returns objects sorted by DateOf Insertion
- (NSArray*)arraySortedByDateOfInsertion {
NSSortDescriptor *descriptor = [NSSortDescriptor sortDescriptorWithKey:#"dateOfInsertion" ascending:NO];
return [self sortedArrayUsingDescriptors:[NSArray arrayWithObject:descriptor]];
}
Hope this helps
You cannot decide where to add element in the table. But you use array for this . You can add the element at the top of the array so it will appeared as a first row in the table :
- (void)insertObject:(id)anObject atIndex:(NSUInteger)index

Resources