Add items from NSSet to UITableView (convert NSSet to multiple arrays?) - ios

I have a UITableView that adds information from a Core Data the following way:
The "Category" for various names is added at the header
The names that correspond to the Category should be loaded in cells beneath the Category
Right now I have the header loading the right name and the right number of sections and rows being added. However - the results from Core Data are being returned as an NSSet, so I can't add each name to the cells based on the indexPath.row.
Any suggestions? For reference:
cell.textLabel.text = [NSString stringWithFormat:#"%#",[[[dataToUse objectAtIndex:indexPath.section]valueForKey:#"heldBy"]valueForKey:#"name"]];
Returns the appropriate set of names to each cell of the appropriate section, but it returns the ENTIRE set to each cell. I just want each name to be added based on which row it's a part of. I can solve this by converting the NSSet to an Array, but since there are multiple sets being created (because there are multiple categories) I don't see how I can do this.
EDIT: I fixed my problem by doing the following, but I'm still interested to know what the best thing to do would have been.
NSSet *daset = [[dataToUse objectAtIndex:indexPath.section]valueForKey:#"heldBy"];
NSMutableArray *addToLabel = [[NSMutableArray alloc]init];
int i = 0;
for(NSSet *contact in daset) {
[addToLabel insertObject:[contact valueForKey:#"name"] atIndex:i];
NSLog(#"%#",addToLabel);
i++;
}
cell.textLabel.text = [NSString stringWithFormat:#"%#",[addToLabel objectAtIndex:indexPath.row]];

Use an NSFetchedResultsController
It's designed to do exactly what you're looking for. For straight forward cases like yours, where you just need your data organized into sections based on your model relationships it will offload a lot of weight off your shoulders by automatically managing the fetching, editing, caching etc. You can find a nice tutorial here and of course the official documentation here.

To convert from NSSet->NSArray you need to sort the set with a NSSortDescriptior. Something like:
NSSortDescriptor *sort=[[NSSortDescriptor alloc] initWithKey:#"nm" ascending:YES selector:#selector(localizedCaseInsensitiveCompare:)];
NSArray *arr = [[myset allObjects] sortedArrayUsingDescriptors:[NSArray arrayWithObject:sort]];

NSSortDescriptor *sort=[[NSSortDescriptor alloc] initWithKey:#"name" ascending:YES selector:#selector(localizedCaseInsensitiveCompare:)];
NSArray *arr = [[myset allObjects] sortedArrayUsingDescriptors:[NSArray arrayWithObject:sort]];
"nsset object" *nssetObject =[arr objectAtIndex:indexPath.row];
cell.textLabel.text = [NSString stringWithFormat:#"%#",nssetObject.name];

Related

Objective-C, Sorting an NSMultable array of type class, alphabetically?

I'm trying to sort a property on a type of a mutable array.
However I've only managed to sort a NSString array.
NSMutableArray<DBFILESFileMetadata*> *tmpArray = [[NSMutableArray alloc]init];
for (DBFILESMetadata *entry in entries)
{
//conditions
[tmpArray addObject:fileMetadata];
}
Here's the type / class
https://github.com/dropbox/dropbox-sdk-obj-c/blob/4c99bdf726cf9724adfddc19e71a87a6012eddeb/Source/ObjectiveDropboxOfficial/Shared/Generated/ApiObjects/Files/Headers/DBFILESMetadata.h
I've tried
[yourArray sortUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
and
NSSortDescriptor *sort = [NSSortDescriptor sortDescriptorWithKey:#"name" ascending:YES];
[yourArray sortUsingDescriptors:[NSArray arrayWithObject:sort]];
The property is called name.
I've seen answer like this How can I sort an NSMutableArray alphabetically? but I can't seem to get this to work for my scenario.
In order to sort using sortUsingSelector you need to implement your compare method in the objects that you are trying to compare. (So your DBFILESMetadata class would need a compare method - localizedCaseInsensitiveCompare in the code above.)
You should be able too use a sort descriptor as you show in your second attempt. What happens when you do that?
A third way to do it is to use the NSMutableArray sortUsingComparator and write an NSComparator block that compares the 2 objects.
I'm out of practice with Objective-C but a quick Google search found an example, which I adapted to your specific problem:
[entries sortUsingComparator:
^NSComparisonResult(DBFILESMetadata *obj1, DBFILESMetadata *obj2)
{
return [obj1.name localizedCaseInsensitiveCompare: obj2.name];
}];
That should work, although as I say I'm out of practice in Objective-C, and its block syntax is pretty awkward and counter-intutive.

sorting array of objects by property

I'm new to ios development and I'm trying to sort a tableview that gets populated by an array. I've looked into other solutions and for some reason the tableview isn't populating correctly. The array sorts in ascending order, but then the tableview doesn't display the objects in the correct order.
Here is what I have for the sorting:
NSSortDescriptor *sortDescriptor;
sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"_miles"
ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
NSArray *sortedArray = [_addObjects sortedArrayUsingDescriptors:sortDescriptors];
_objects = [NSMutableArray arrayWithArray:sortedArray];
Here is my cellforrow :
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//NSLog(#"%#", _objects);
VenuesTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
CustomObject *currentObject = [_objects objectAtIndex:indexPath.row];
cell.venuesTitle.text = [currentObject nameOfVenue];
NSString *preDistance = #"about";
NSString *postDistance = #"miles";
cell.venuesSubTitle.text = [NSString stringWithFormat:#"%# %# %#", preDistance,[_miles objectAtIndex:indexPath.row] ,postDistance];
if (cell != nil) {
NSMutableString *imagePath = [NSMutableString stringWithFormat:#"%#", [_paths objectAtIndex:indexPath.row]];
[cell.venuesImage setContentMode:UIViewContentModeScaleAspectFit];
[cell.venuesImage setImageWithURL:[NSURL URLWithString:imagePath] placeholderImage:[UIImage imageNamed:#"placeholder#2x.png"]];
}
return cell;
}
when I log out the _objects array I get them in the right order, but they're not appearing in the right order in the tableview (the objects have a miles property that I'm trying to sort by).
You are sorting your array, _addObjects by miles and storing the result in _objects, but then, when you're displaying the results, you're not accessing miles from that sorted array you just created, but rather looking it up in some other array.
I don't know when you're populating that separate _miles array, but I'd suggest you retire it entirely and just make sure you get and set the miles property from the CustomObject instances. (Same for _paths ... this should probably be a property of your CustomObject, not a separate array.)
I don't see anything obvious, but a couple things that are a bit strange:
You should not use _objects. Always use self.objects instead. Most programming languages make it completely impossible to access pointers directly like you are doing here, and for good reason. It's dangerous and can lead to confusing bugs like this one. You should pretty much never access a property using _foobar. Always use self.foobar.
Why are you using NSMutableArray instead of NSArray? Are you sure there isn't code somewhere else modifying it?
Where does VenuesTableViewCell *cell get created? I don't see anywhere in your code that one of these objects is created. [tableView dequeueReusableCellWithIdentifier..] never creates a new cell, it always returns an existing one or nil if none exists. Your code isn't throwing an assertion error (I assume?), so obviously it never returns nil. But there's something weird going on if that doesn't return nil the first time you call it?

Core Data objects ordering inside a section

In a previous question, a user asked for a method to order core data objects inside a UITableView Section. I am in the same case, but I don't find the way to implement the proposed solution.
The proposed solution was to create the following method:
- (NSArray*)sortedSectionForIndex:(NSInteger)index
{
NSSortDescriptor *sort = [NSSortDescriptor sortDescriptorWithKey:#"priority" ascending:YES];
id section = [[self fetchedResultsController] sections][index];
NSLog(#"INDEX********* = %ld", (long)index);
NSArray *objects = [section objects];
NSArray *sorted = [objects sortedArrayUsingDescriptors:#[sort]];
return sorted;
}
But I don't know how to call this method inside my code in the way that the section objects would be ordered.
Thank you.
You should implement separate fetchedResultsController(s) and sort descriptor for the section(s) you want sorted differently. Take a look at the CompanyMenuTableViewController code in this sample app. One of the sections uses it's own fetchedResultsController. You would use a similar approach to handle each section independently of other sections, with each section having its own fetchedResultsController if necessary.
Make sure you download the most recent version.
http://ossh.com.au/design-and-technology/software-development/sample-library-style-ios-core-data-app-with-icloud-integration/

Sorting an NSArray and case precedence

I have a list of managed objects stored in core data. I use these objects to populate a tableview controller that is sectioned according to alphabetical order. The data in these objects is obtained via web service, so I have no control over their case (which really doesn't make much difference in this case).
Most of the data is returned in all caps. I've noticed that, on the rare occasions where the case is NOT all caps, those items do not fall into alphabetic order. In the following code sample, stationIndex is an array of sorted first letters:
for(NSString *character in stationIndex){
NSPredicate *pred = [NSPredicate predicateWithFormat:#"name beginswith[c] %#", character];
// sort the list
NSArray *filteredGaugeList = [[tempGaugeList filteredArrayUsingPredicate:pred] sortedArrayUsingComparator:^NSComparisonResult(id a, id b) {
NSString *first = [(Gauge*)a name];
NSString *second = [(Gauge*)b name];
return [first compare:second];
}];
if([filteredGaugeList count] > 0){
[[self allGauges] addObject:filteredGaugeList];
}
}
I'm aware that there is a way to ignore case when using a selector, but in my case, I'm sorting on properties of objects, so I'm assuming I need a comparator. Is there a way to handles case in this situation? Thanks!
You can sort ignoring case in a comparator as well, just use
return [first caseInsensitiveCompare:second];
Alternatively, use a sort descriptor specifying the selector and the sort key:
NSSortDescriptor *sort = [NSSortDescriptor sortDescriptorWithKey:#"name"
ascending:YES
selector:#selector(caseInsensitiveCompare:)];
NSArray *sorted = [array sortedArrayUsingDescriptors:#[sort]];
Remark: To display Core Data objects in a table view, you can also use
NSFetchedResultsController. Then you would add the predicate and the sort
descriptor to the fetch request. A fetched results controller has also methods
to group a table view into sections, and to update a table view automatically
when objects are inserted/deleted/modified.

UITableview, NSArrays and NSDictionnaries // Ordering

I have to UITableview, section headers titles contained in a dictionnary, contents in an array associated with each title (those arrays create the cells).
It is ok that many have answered here about ordering dictionnary, that is was pretty difficult, etc…
Even if a dictionnary can('t, or with difficulties) be ordered, how does it keeps the same order everytime ?
example
Let's say i end up with a table view with two sections (titled), each containing some cells
A dictionnary is declared, it contains the section titles.
NSMutableDictionary *menuEntries;
For each of those dictionnary entries, a different array associated (which then is used to create and populate the cells). We will have two sections (so two keys in the dictionnary), for some reasons we use two different array that we are going to associate to those keys
NSArray *mainMenuArray;
NSMutableArray *magazineMenuArray;
The first one is populated like this (
mainMenuArray = [[NSArray alloc] initWithObjects: btn1,btn2,btn3,btn4,nil];
The second array (magazineMenuArray) is populated via some json call (not showing here how, but everythings works fine)
So we end up up setting the dictionnary
menuEntries = [[NSMutableDictionary alloc] init];
[menuEntries setObject:mainMenuArray forKey:#"First section"];
[menuEntries setObject:self.magazineMenuArray forKey:#"Second section"];
In the end, it works pretty well, we have defined some attributes for the arrays' object, one for the title, one for the action to be called, pretty cool.
!! BUT !!
Second section appears before first section. Nothing to do about/against it. Always.
I can hear that a NSDictionnary CAN'T BE ORDERED, ok, but I reeeeeeally feel like, in that case, IT IS ORDERED somehow.
That is very confusing.
NSDictionary keeps the same order every time, but that is an arbitrary order based on the hash codes of the objects that you insert as keys. If the dictionary is mutable, inserting or removing objects can change the ordering of the keys that are already in the dictionary.
Although it is not possible to order the dictionary itself, it is certainly possible to order its keys into a separate array, and then walk that array in order, pulling the objects by key from the(unordered) dictionary.
Edit:
You say that you have
NSMutableDictionary *menuEntries;
Which is populated as:
menuEntries = [[NSMutableDictionary alloc] init];
[menuEntries setObject:mainMenuArray forKey:#"First section"];
[menuEntries setObject:self.magazineMenuArray forKey:#"Second section"];
If you want it to respect the order in which you populate it, you should use a NSMutableArray instead, e.g.:
NSMutableArray *menuEntries;
And then, you can populate that array with dictionary entries with, at the very least, two keys, something for the title of the section and something with the rows for that section. Thus:
menuEntries = [[NSMutableArray alloc] init];
[menuEntries addObject:[NSDictionary dictionaryWithObjectsAndKeys:
#"First section", #"title",
mainMenuArray, #"rows",
nil]];
[menuEntries addObject:[NSDictionary dictionaryWithObjectsAndKeys:
#"Second section", #"title",
self.magazineMenuArray, #"rows",
nil]];
Thus,
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [menuEntries count];
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
NSDictionary *section = [menuEntries objectAtIndex:section];
return [section objectForKey:#"title"];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSDictionary *section = [menuEntries objectAtIndex:section];
return [[section objectForKey:#"rows"] count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSDictionary *section = [menuEntries objectAtIndex:indexPath.section];
NSArray *rows = [section objectForKey:#"rows"];
id row = [rows objectAtIndex:indexPath.row];
// I didn't know how mainMenuArray and self.magazineMenuArray were populated,
// so I used a data type of `id` for the row, but you can obviously replace
// that with whatever is appropriate, e.g., NSDictionary* or whatever.
// proceed with the configuring of the cell here
}
Personally, I wouldn't use the literal strings #"title" and #"rows" all over the place, but rather define constants like the following, include these at the start of the implementation, and use them instead of the literal strings. But I'm sure you get the basic idea.
NSString * const kTableTitleKey = #"title";
NSString * const kTableRowsKey = #"rows";
Regardless, this outlines a very common data model I use behind my UITableView objects. It's a nice logical structure that corresponds to the table view itself. Essentially it is an array of sections, each of which is a dictionary with two keys, one for the title and one for the rows of the section. The value for that "rows of the section" is, itself, an array, one entry for every row of the table. It sounds complicated, but as you see above, it actually makes the implementation very, very simple.
My original answer was provided before OP supplied any information about the nature of the data structures. Thus I provided an answer to the more abstract question of how does one sort an array of dictionary entries. I retain that answer for historical reference, though:
Original answer:
I'm not sure how you are storing your dictionary and how you represent rows in your table, but a common pattern is to have an array of dictionary items:
NSArray *array = #[
#{#"id" : #"1", #"name":#"Mo", #"age":#25},
#{#"id" : #"2", #"name":#"Larry", #"age":#29},
#{#"id" : #"3", #"name":#"Curly", #"age":#27},
#{#"id" : #"4", #"name":#"Shemp", #"age":#28}
];
You can then sort that via name, like so:
NSSortDescriptor *descriptor = [[NSSortDescriptor alloc] initWithKey:#"name"
ascending:YES];
NSArray *sortedArray = [array sortedArrayUsingDescriptors:#[descriptor]];
NSLog(#"array = %#", array);
NSLog(#"sortedArray = %#", sortedArray);
There are a whole series of sorting methods, so check out Sorting in the NSArray Class Reference.

Resources