Sorting array of custom objects into sections dynamically UITableview - uitableview

I have a problem that's being plaguing me for a few days now.
I have an array called countries. This array contains a custom object which holds data such as Country, City, ip, status etc.
I need to present these objects in a tableview sectioned by the property "country".
This means that if I have two custom objects both with the country "Australia" I need to add it to a section called "Australia" and if I have 5 objects with the country "Spain" I need those 5 objects to be in the Spain section.
I need to find a way to make sure that even if an object is added with a different country it goes under a section with it's country name country name.
I've tried a variety of things but nothing has come even close to fixing this.
The reason why this is dynamic is because I don't know how many objects are going to be in each section. This means the number of objects in each section can change.
I used this code to sort the array alphabetically I just need to put each object into a section based on the property "Country" I do not know how many sections there will be or how many objects will be in each section.
self.countries = self.countries.sorted { $0.country < $1.country }
Could anyone give me a hand?
edit: added my cellForRowAtIndexPath.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if recievedData == true {
var cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! CountryListCell
var serverInfo = countries[indexPath.row] as ServerInfoObject
cell.countryTitle.text = serverInfo.country
cell.flagImage.image = UIImage(named: serverInfo.flag)
cell.serverInfo = serverInfo
return cell
}
return CountryListCell()
}

So you need to sort you servers into a dictionary. E.G [String:[Server]] the string being the section name (country). So an example dict ["Albania":[serv1,server2,...]]
You can simply do this by sorting through the server array (I assume you can figure this out).
You will also need to save an array of your section titles so that you can access it in cellForRowAtIndexPath. For example ["Albania","Brazil","Cuba","Germany","Russia"]
So basically when in cellForRowAtIndexPath you access the current country with
var country = yourSectionArray[indexPath.section]
Then access the array of servers that are valid to that country through your dictionary like so:
var serversOfCurrentSection = yourDictionary[country] as [Server]
Then to get the current server for the index just do
var server = serversOfCurrentSection[indexPath.row]
To get the number of objects in each section:
var country = yourSectionArray[indexPath.section]
yourDictionary[country].count

Did you try to use NSPredicate to help you filter your array by country name? I think you can either split your servers into an array of array on the run or at the beginning of load, depending on whether the list updates.
In numberOfRowsInSection:, just count the rows of the sub array at index section.
Similarly, in cellForRowAtIndexPath:, use your sorted arrays to get info from your custom class.
I had to deal with something similar with sorting contacts. Hopefully this helps!

There are lots of ways to do this.
Table views need stable data in order to work. If you change your data model, you need to tell your table view to reload.
I'd probably store the records 2 ways: In a flat array, and then in an array by countries where each entry in that array was an array of the records for that country. Collection objects like arrays save the objects inside them by reference, so there's not a lot of overhead in storing the same records in multiple arrays.
That makes creating your sectioned table view very clean.
You could sort your flat array of record by country, and then do a pass through the sorted array, building your arrays of records by country. If you need your records sorted by some other criteria within a country you might want to use an NSPredicate to do the sorting rather than sorted, since it can take an array of sort keys.
If the user can enter a new record at any spot, you could collect the data for that new record, then find the spot where it belongs in the sorted array, insert it at that spot, and then rebuild your 2 dimensional array of arrays from the sorted flat array. That would avoid re-sorting the entire array, which can be slow for large arrays of data.)

Related

Sort Order vs Display Sort Order

I have an app that displays a section title followed by the detail items followed by the next section and its details. Everything works fine, but I would like to rearrange the order in which the sections are shown. The problem is that I need to order the core data by the report-id then status then the date to get the correct detail items to show under the proper section.
let sortDescriptor1 = NSSortDescriptor(key: #keyPath(Item.report.id), ascending:true)
let sortDescriptor2 = NSSortDescriptor(key: #keyPath(Item.report.status), ascending:true)
let sortDescriptor3 = NSSortDescriptor(key: #keyPath(Item.report.dateStarted), ascending:false)
let sortDescriptor4 = NSSortDescriptor(key: #keyPath(Item.date), ascending:true)
How can I change the sort/display order while still maintaining the proper relationship between the section (report) and the detail items associated with it? The report.id is a UUID so currently the reports end up in random order.
A fetchedResultsController has a property sectionNameKeyPath which can be used to group items togethers. This only works if the sectionNameKeyPath groups the items in the same order that they are sorted. For example: you can sort by date and then group by hour or week or any other time based grouping, but not by name. In your case you want the sections to be sorted in a way that does not not match how the items are grouped. There may be some clever solution for your particular situation, but since you didn't give any details I can only give a general solution.
The indexPath that is returned from a fetchedResultsController is really useful for interoperability with a tableView or collectionView. But it does not have to be a one-to-one relationship. For example you could have a setup where one section points to one fetchedResultsController and another section points to a different one. The key in doing this setup is to make sure to not confuse the fetchedResultsController indexPath with your collectionView indexPath. Generally I find having a separate object to manage converting the indexPath the easily way to keep it straight.
Create a separate object that sorts the sections after the fetchedResultsController does a fetch (and after a section is inserted or deleted). Inside the indexPathsManager have a dictionary the maps between the "local" indexPath and the fetchedResultsController indexPath. Make sure to sure use this object EVERY TIME you use indexPaths in the viewController. Also make sure to convert the indexPaths when you update the view after the fetchedResultsController delegates that there is a change. And updating the indexPathsManager when any sections are inserted or deleted.
TL;DR Sort the sections of the fetchedResultsController after the fetch and convert your tableView's indexPath to fetchedResultsController indexPaths.

Search for the index of a Core Data array that contains a certain date attribute

I have been looking around everywhere online and can't seem to get a solid answer. If I have an event with multiple attributes stored in Core Data in an array and I can display all of these individual events in a tableView. Lets say one of the attributes is a date. Is there away to search for all of the events that contain a certain date and display only those in the tableView? I was thinking of using NSPredicate from what I am seeing online but I am not familiar with this. Maybe somehow find the index of the event that contains that date and only display that index? Any ideas?
Table views are designed to implement a one to one relationship with the underlying array of data. The usual approach is to use a fetchRequest to only get the data you want to display ( I personally like to have all my requests pre-defined in the data model so I can keep track of how data is accessed).
But, if your array of managed record must contain more elements than what you want to display, you should consider basing your tableView datasource responses on an intermediate array that only contains (or refers to) the objects you want to show.
For example:
let allEvents:[EventRecord] = GetAllEvents()
var filteredEvents:[EventRecord] = allEvents.filter({ $0.eventDate.isEqualToDate(dateToDisplay) })
and use filteredEvents as your underlying store for your datasource.
This will make it easy to dynamically change the filtering conditions and reload the tableview without having to go back to the database for each filter change.

Problems With sorting array data coming from json (all values)

I have 4 values coming from json (name, ratings, reviews and Qualifications). I want to sort this data using name, reviews and qualifications one by one. But when I sort this data then remaining values are not changing in array.
Here is my code.
_arrOfDoc_name = [_arrOfDoc_name sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
From comments:
I have to show four values on my screen which are coming from 4 different arrays. but when i sort one array then values of another arrays are not gng to sort and it displays wrong data on screen. how can i sort data of remaining arrays using its index path.
Ok, your comments explain what you're trying to do.
The best answer is to NOT use 4 different arrays. Restructure your data to have a single array that contains dictionaries. Then sort the array of dictionaries.
Failing that, you'll have to be clever. You might be able to create an array of indexes and sort that, or hand-write a sort routine that sorts all 4 arrays at once.

Get index for value NSDictionary

I've got a value like so: #"2329300" and I've got a NSDictionary like so :{#"John Appleseed":[#"2329300",#"2342322",#"32i249"]}
How do I find the index of the key/value pair in the NSDictionary when I've only got a string value of the entire list that's known as the value. I'm assuming there's no duplicates in the dict.
I know that there's indexForObject on a NSArray but is there a similar thing for a dict?
I imagine it would look something like this:
[NSDictionary indexForValue:value]; // returns index number.
And even then the NSString doesn't match the value, so I'd need a workaround for that too.
You have a basic misunderstanding. Dictionaries are unordered collections. They do not have any particular order for their key/value pairs. You can't have indexes to the key/value pairs because that implies a fixed order.
Think of a dictionary as a bunch of kids milling around on a playground. You can call out a kid's name "Johnny, come here!" and fetch that kid (use a key to find an object) but what does order mean for kids that won't sit still?
You can create an array of the keys from a dictionary and sort that into a particular order (an alphabetical list of the kids on the playground) if that's what you want, or you can create an array of dictionaries, or an array of a custom data object that contains any arbitrary properties that you want.
EDIT:
For a table view, an array of dictionaries is probably a good choice. Each entry in the array contains a dictionary with all the settings for a cell in the dictionary. If you have a sectioned table view then you want an outer array for sections, containing inner arrays for the rows, and each entry in the inner array containing a dictionary.
I tend to prefer custom data objects to dictionaries though. (An object that just has properties for each setting I want.) That way the list of values and their types is crystal-clear and fairly self-documenting.

Data representation suited for UITableViewDataSource

Currently, I have the following set up in my app:
For example, I have a gamelist, where the games have different states and I would like a certain state to be in a certain section etc.
Since I have 3 different states I'll use a NSDictionary with State as key and Array as value and put a certain game with a certain state in the correct array.
Then my code for numberOfRowsInSection would be something like:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSString* state = [self getStateForSection:section];
return [[obj objectForKey:state] count];
}
The problem is I dislike having the objects in arrays since then I have to look through the whole array whenever I want to access a certain match.
I would instead like to have all matches in a single NSDictionary with matchID as key and NSDictionary with as value (the match).
But how would I do in UITableView then?
I could do something where I return allKeys of my NSDictionary but I still have to iterate it to find their current state and add their key to the appropriate list?
You could just also build a NSDictionary from matchID to match on the side as well, simply reusing the same objects and keeping the NSDictionary of NSArrays just for the table data source. Then the problem is that you have to update both when the data changes. You may also want to look into NSArray filteredArrayUsingPredicate:
Another possibility is using another data structure known as the sorted dictionary which keeps its keys in a specific order. That way you can ask for all the values as an array for the table view and get a constant order, but still be able to do a lookup with a matchID directly to a match.

Resources