- (NSArray *)combinedStrings {
return [[self.numberOfUsers arrayByAddingObjectsFromArray:self.numberOfModerators] arrayByAddingObjectsFromArray:self.numberOfAdmins];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSDictionary *jsonForJam = [self.combinedStrings objectAtIndex:indexPath.row];
return cell;
}
I would like to sort the result displayed in the tableView in descending order, how can I do such thing ?
I've already tried to use that but it was not helpful : Best way to sort an NSArray of NSDictionary objects?
A solution based on the sample code I provided will be very helpful.
You can use the following code for sorting array of strings.
self.combinedStrings = [[[self.combinedStrings sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)] reverseObjectEnumerator] allObjects];
or
NSSortDescriptor* sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:nil ascending:NO selector:#selector(localizedCompare:)];
self.combinedStrings = [self.combinedStrings sortedArrayUsingDescriptors:[NSArray arrayWithObject:sortDescriptor]];
A) you can implement your own custom sorting method (that still ends up using iOS sorting methods in the end) because the NSDictionary complicates matters.
or
B) create an object (its purpose is to hold all the info in the dictionaries) then store the custom objects in the array, then sort it like this or in any matter you wish:
self.combinedStrings = [self.combinedStrings sortedArrayUsingComparator:^(id obj1, id obj2)
{
Client *client1 = (Client*)obj1;
Client *client2 = (Client*)obj2;
if([client1.firstName isEqualToString:client2.firstName])
return [client1.lastName compare:client2.lastName];
else
return [client1.firstName compare:client2.firstName];
}];
Here "Client" would be the custom object and the data is stored in properties for easy access.
Hope this helps
Related
I am storing JSON Data to CoreData with a one-to-many relationship. I am able to get the data back, using NSFetchRequest and fast enumeration, but the data is not coming in the order format I need and it cannot be used in my UITableViewCells how can i do this
this is my code
this is my datamodel
https://www.dropbox.com/s/1e1ujrjxtkjy9h9/Screen%20Shot%202015-04-29%20at%205.14.31%20pm.png?dl=0
_appDelegate = [[UIApplication sharedApplication]delegate];
_managedObjectContext = [_appDelegate managedObjectContext];
NSFetchRequest * fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"DealSection"];
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:#"sectionID == %#",_sectionID]];
[fetchRequest setReturnsObjectsAsFaults:NO];
NSArray * sortDescriptor = [NSArray arrayWithObjects:nil];
[fetchRequest setSortDescriptors:sortDescriptor];
NSError * error;
NSArray * data = [_managedObjectContext executeFetchRequest:fetchRequest error:&error];
_fetchData = data;
for (DealSection * section in _fetchData) {
for (Deal * deal in [section alldeals]) {
NSLog(#"%#",[deal nameAttribute]);
}
}
i put this all code in the ViewDidLoad
here am getting the data from NSLog but my problem am able to print the data but am not able to pass the data to the table view
The JSON snippet that you have posted shows that (contrary to my supposition in the comments, apologies) each Deal can have many DealSections.
Your DealSection entity has a relationship entitled alldeals that is currently a to-one relationship. I.e. each DealSection can have only one Deal. I think this should be to-many. To take an example, the deal in the JSON you posted has sections with ID=6 (name="Services") and ID=8 (name="Wellness"). Suppose you had another deal with a section with ID=6 - do you want to use establish the relationship with the existing DealSection, or to create a new DealSection?
Currently your code creates a new DealSection, but I think you probably want the relationship with the existing DealSection. For that to work, you will need to
a) amend your data model to make the relationship many-many.
b) amend the code where you store the data for the many-many relationship, so that it starts by trying to fetch a DealSection with the correct ID. If one is found, add that DealSection to the Deal's sectionRelation. If it is not found, created a new DealSection and add that to the Deal.
EDIT
To display the data in a tableView, you need to implement three datasource methods:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// A table view section for each DealSection:
return [self.fetchData count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
DealSection *dealSection = self.fetchData[section];
NSArray *deals = [dealSection.alldeals allObjects];
return [deals count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// Amend this identifier to match your cell prototype
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
// Set up the cell...
DealSection *dealSection = (DealSection *)self.fetchData[indexPath.section];
NSArray *deals = [dealSection.alldeals allObjects];
Deal *deal = (Deal *)deals[indexPath.row];
cell.textLabel.text = deal.nameAttribute;
return cell;
}
This is basic table view stuff, so if any of it is unclear I recommend the tutorial I mentioned in comments.
I am trying to display sections and rows correctly for my uiTableView.
I have had great help from one contributor and am fairly close to fixing my issue. The Issue can be seen here. Its not far off being right, its just the sections that need to be sorted.
It is repeating the section titles instead of only showing it once. Im not sure exactly how to fix this.
// Find out the path of recipes.plist
NSString *path = [[NSBundle mainBundle] pathForResource:#"lawpolice" ofType:#"plist"];
// Load the file content and read the data into arrays
self.dataArray = [NSArray arrayWithContentsOfFile:path];
//Sort the array by section
self.sortedArray = [self.dataArray sortedArrayUsingDescriptors:#[
[NSSortDescriptor sortDescriptorWithKey:#"Section" ascending:YES],
[NSSortDescriptor sortDescriptorWithKey:#"Title" ascending:YES]]];
self.temp = [[NSMutableDictionary alloc] init];
for (NSDictionary *dict in self.sortedArray) {
NSMutableArray *array = self.temp[dict[#"Section"]];
// No items with the same section key stored yet, so we need to initialize a new array.
if (array == NULL) {
array = [[NSMutableArray alloc] init];
}
// Store the title in the array.
[array addObject:dict[#"Title"]];
// Save the array as the value for the section key.
[self.temp setObject:array forKey:dict[#"Section"]];
}
self.policePowers = [self.temp copy]; // copy returns an immutable copy of temp.
//Section for sorting
self.sectionArray = [self.sortedArray valueForKeyPath:#"Section"];
NSLog(#"%#", self.sectionArray);
//Title
self.namesArray = [self.sortedArray valueForKeyPath:#"Title"];
//Offence
self.offenseArray = [self.sortedArray valueForKeyPath:#"Offence"];
//Points to Prove
self.ptpArray = [self.sortedArray valueForKeyPath:#"PTP"];
//Action
self.actionsArray = [self.sortedArray valueForKeyPath:#"Actions"];
//Notes
self.notesArray = [self.sortedArray valueForKeyPath:#"Notes"];
//Legislation
self.legislationArray = [self.sortedArray valueForKeyPath:#"Legislation"];
//PNLD
self.pnldArray = [self.sortedArray valueForKeyPath:#"PNLD"];
//Image
self.imageString = [self.sortedArray valueForKeyPath:#"image"];
titleForHeaderInSection
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
return [self.sectionArray objectAtIndex:section];
}
numberOfSectionsInTableView
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [self.policePowers count];
}
numberOfRowsInSection
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSArray *sectionrows = self.policePowers[self.sectionArray[section]];
return [sectionrows count];
}
Update
To be clear, if two items have the same Section value, I want to automatically group them into an array and have that array mapped to the Section value at the end
NSDictionary dictionaryWithObjects:forKeys: basically loops through two arrays and maps the object in one array at the current index as the key for the object in the other array at the same index. When you're calling
self.policePowers = [NSDictionary dictionaryWithObjects:self.namesArray forKeys:self.sectionArray];
it therefore maps the items in self.sectionArray as the keys for the items in self.namesArray. Looking at your plist file, the "Title" keypath (which is mapped to self.namesArray) has a value of string, so your NSLog results make sense, as self.namesArray is an array of strings, not an array of arrays.
I'm not sure how you were supposed to get a result like
"Alcohol: Licensing/Drive unfit" = {
"Drive/attempt to drive/in charge whilst unfit or over",
"Drive/attempt to drive/in charge whilst unfit or over",
"Drive/attempt to drive/in charge whilst unfit or over",
}
Where is that array supposed to come from?
-- EDIT --
I don't think there's a concise way to accomplish what you want, so it'd have to be done manually. I haven't actually used [NSArray arrayWithContentsOfFile:path] before, so is self.dataArray an array of dictionaries with each item representing one of the items in the plist (Item 44, Item 45, etc)? If so, you could do something like this:
NSMutableDictionary *temp = [[NSMutableDictionary alloc] init];
for (NSDictionary *dict in self.dataArray) {
NSMutableArray *array = temp[dict[#"Section"]];
// No items with the same section key stored yet, so we need to initialize a new array.
if (array == null) {
array = [[NSMutableArray alloc] init];
}
// Store the title in the array.
[array addObject:dict[#"Title"]];
// Save the array as the value for the section key.
[temp setObject:array forKey:dict[#"Section"]];
}
self.policePowers = [temp copy]; // copy returns an immutable copy of temp.
-- EDIT AGAIN --
The app crashes because self.policePowers is an NSDictionary, not an NSArray; thus it doesn't have an objectAtIndex: method. If you're trying to get the section title, try this instead:
return [self.sectionArray objectAtIndex:section];
Furthermore, if you're working with a table view, I'd basically have self.sectionArray sorted whichever way you like, then whenever I needed to populate data in each section, I would use self.policePowers[self.sectionArray[section]] to return the array of titles mapped to that section title.
-- YET ANOTHER --
If you break it up into the following lines, where is the NSRangeException thrown? If you NSLog, do the results match what you expect?
NSString *title = self.sortedKeys[indexPath.section];
NSArray *array = self.policePowers[title];
NSString *value = array[indexPath.row];
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?
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.
I have the following to lines to sort and section out my tableview.
NSSortDescriptor *sortDescriptorState = [[NSSortDescriptor alloc] initWithKey:#"positionSort" ascending:YES];
The above is a integer value that sorts out my cells by their respective positionSort value.
I also have the below code to display section names; however, the sections still appear in alphabetical order instead of in order of positionSort. How can I correct this?
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:#"position" cacheName:#"Master"];
Thanks!
UPDATE: Thanks to #MartinR I was able to arrive at an answer.
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:section];
// Here I build up one of my Position objects using my unique position passed.
// I had to cast the object as NSMutableString* to get rid of a warning
Position * aPosition = [Position positionWithUniquePosition:(NSMutableString *)[[[sectionInfo objects] objectAtIndex: 0] position]
inManagedObjectContext:self.managedObjectContext];
// Once the above is done then I simply just accessed the attribute from my object
return aPosition.positionDescription;
}
From the initWithFetchRequest:managedObjectContext:sectionNameKeyPath:cacheName: documentation:
sectionNameKeyPath
... If this
key path is not the same as that specified by the first sort
descriptor in fetchRequest, they must generate the same relative
orderings. For example, the first sort descriptor in fetchRequest
might specify the key for a persistent property; sectionNameKeyPath
might specify a key for a transient property derived from the
persistent property.
So you cannot use "positionSort" in the sort descriptor and "position" in the sectionNameKeyPath, because sorting numbers and sorting strings does not generate the same relative orderings.
I would use "positionSort" for both and change tableView:titleForHeaderInSection: so that it returns the position name as section title instead of the position number.
I did not try this myself but something like this should work:
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
id <NSFetchedResultsSectionInfo> sectionInfo = [[self.controller sections] objectAtIndex:section];
return [[[sectionInfo objects] objectAtIndex:0] position];
}