I am trying to populate data of restaurants from Parse with the column "Category" data as section heading for the list of items. However the section titles for the items are getting displayed randomly. I want them to be displayed as they are stored in the backend. I tried doing [query orderByAscending:#"Category"]; which sorts the Categories alphabetically and has no change in the results. The below is my current implementation...
-(void) listAllItems {
PFQuery *query = [PFQuery queryWithClassName:[NSString stringWithString:self.restaurantName]];
[query whereKey:#"DinnerLunch" containsString:self.lunchDinnerPreference];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
for (PFObject *obj in objects) {
self.curatedItemDictionary = [[NSMutableDictionary alloc]init];
[_curatedItemDictionary setValue:[obj objectForKey:#"ItemName"] forKey:#"ItemName"];
[_curatedItemDictionary setValue:[obj objectForKey:#"Price"] forKey:#"Price"];
[_curatedItemDictionary setValue:[obj objectForKey:#"Category"] forKey:#"Category"];
[_curatedItemDictionary setValue:#"" forKey:#"ModifiableItems"];
_sectionsArray = [_sectionsDictionary objectForKey: [obj objectForKey:#"Category"]];
if(nil == _sectionsArray){
self.sectionsArray = [[NSMutableArray alloc]init];
}
[_sectionsArray addObject:_curatedItemDictionary];
[_sectionsDictionary setObject: _sectionsArray forKey: [obj objectForKey:#"Category"]];
NSLog(#"categories keys are %#", [self.sectionsDictionary allKeys]);
}
[self.tableView reloadData];
} else {
// Log details of the failure
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return [_sectionsDictionary count];
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section{
NSArray *keys = [self.sectionsDictionary allKeys];
return [keys objectAtIndex:section];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSArray *keys = [self.sectionsDictionary allKeys];
id aKey = [keys objectAtIndex:section];
NSMutableArray *arr = [self.sectionsDictionary objectForKey:aKey];
return (arr) ? arr.count : 0;
}
I fixed it by creating a new NSMutableArray. Actually i read it from this post How can i get Original order of NSDictionary/NSMutableDictionary?
I added this line while iterating through my PFObjects [_categoryMutableArray addObject:[obj objectForKey:#"Category"]]; and then i used the below lines in my titleForHeaderInSection and numberOfRowsInSection
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section{
return [_categoryMutableArray objectAtIndex:section];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// NSArray *keys = [self.sectionsDictionary allKeys];
id aKey = [_categoryMutableArray objectAtIndex:section];
NSMutableArray *arr = [self.sectionsDictionary objectForKey:aKey];
return (arr) ? arr.count : 0;
}
I am trying to follow this post http://www.appcoda.com/ios-programming-index-list-uitableview/ to create sections using UITableViewController.
I am using two NSMutableDictionaries first(_curatedItemDictionary) dictionary is to store key value pairs from Parse and second (_sectionsDictionary) is to store these in dictionary as key and values as Category type.
[_sectionsDictionary setObject:[obj objectForKey:#"Category"] forKey:_curatedItemDictionary];
I did this because i couldn't store the key as category and value as dictionary as dictionaries will not support duplicate keys.
I am getting numberOfSectionsInTableView by using the below code
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
for(NSString *key in [_sectionsDictionary allKeys]) {
[_cleanArray addObject:[_sectionsDictionary objectForKey:key]];
}
NSCountedSet *set = [[NSCountedSet alloc] initWithArray:_cleanArray];
for (id item in set) {
NSInteger count = (unsigned long)[set countForObject:item];
[_rowForIndexArray addObject:[NSNumber numberWithInteger:count]];
}
_cleanSectionArray = [[NSSet setWithArray:_cleanArray] allObjects];
return [_cleanSectionArray count];
}
How do i get numberOfRowsInSection from the above information ? How do i know which items are being iterated for a particular section ?
Here is the code I am using to create curatedItemDictionary from Parse.com data
PFQuery *query = [PFQuery queryWithClassName:[NSString stringWithString:self.classname]];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
// The find succeeded.
for (PFObject *obj in objects) {
self.curatedItemDictionary = [[NSMutableDictionary alloc]init];
[_curatedItemDictionary setValue:[obj objectForKey:#"ItemName"] forKey:#"ItemName"];
[_curatedItemDictionary setValue:[obj objectForKey:#"Price"] forKey:#"Price"];
[_sectionsDictionary setObject:[obj objectForKey:#"Category"] forKey:_curatedItemDictionary];
[self.tableView reloadData];
} else {
// Log details of the failure
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];
Based on the code provided in the comment to the original post something similar to that, just an alternative:
self.sectionsDictionary = [NSMutableDictionary new];
NSMutableArray *sectionsArray;
for (PFObject *obj in objects) {
sectionsArray = [self.sectionsDictionary objectForKey: [obj objectForKey:#"Category"]];
if (nil == sectionsArray) sectionsArray = [NSMutableArray new];
[sectionsArray addObject: obj]
[self.sectionsDictionary setObject: sectionsArray forKey: [obj objectForKey:#"Category"]];
}
Then in your numberOfSectionsInTableView you can get the amount of sections just by accessing count method on NSMutableDictionary
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [self.sectionsDictionary count];
}
And then if you need number of rows in each particular section:
- (NSInteger) tableView:(UITableView*)tableView numberOfRowsInSection:(NSInteger)section
{
NSArray *keys = [self.sectionsDictionary allKeys];
id aKey = [keys objectAtIndex:section];
NSMutableArray *arr = [self.sectionsDictionary objectForKey:aKey];
return (arr) ? arr.count : 0;
}
But of course you can loop through the keys in your sectionsDictionary when preparing the datasource and put section index with some key, so you do not need to do use the allKeys trick.
I Had an exception in the line [array removeObjectsInArray:toRemove]; in the method below and can't understand what's wrong with it..
- (void) handleDearchForTerm:(NSString *)searchTerm
{
NSMutableArray *sectionsToRemove = [[NSMutableArray alloc] init];
[self resetSearch];
for (NSString *key in _keys)
{
NSMutableArray *array = [_names valueForKey:key];
NSMutableArray *toRemove = [[NSMutableArray alloc] init];
for (NSString *name in array)
{
if ([name rangeOfString:searchTerm options:NSCaseInsensitiveSearch].location == NSNotFound)
[toRemove addObject:name];
}
if ([array count] == [toRemove count])
[sectionsToRemove addObject:key];
[array removeObjectsInArray:toRemove];
}
[_keys removeObjectsInArray:sectionsToRemove];
[_table reloadData];
}
Probably array is just instance of NSArray, but not NSMutableArray, you shall check _names setObject:forKeys:
Change:
NSMutableArray *array = [_names valueForKey:key];
To:
NSMutableArray *array = [[NSMutableArray alloc] initWithArray:[_names valueForKey:key]];
Or:
NSMutableArray *array = [[_names valueForKey:key] mutableCopy];
I am trying to add the image for search (magnifying glass) as the section, to easily go to the search bar.
This is the issue I am experiencing:
My code to setup the sections is as follows:
Note: self.fbFriends is an Array of Dictionaries that include the facebook friends of the user.
self.fbFriends = [[NSArray alloc] initWithArray:[[MESCache sharedCache] facebookFriends]];
self.searchResults = [[NSMutableArray alloc] init];
self.sections = [[NSMutableDictionary alloc] init];
BOOL found;
[self.sections setValue:[[NSMutableArray alloc] init] forKey:UITableViewIndexSearch];
// Loop through the friends and create our keys
for (NSDictionary *friend in self.fbFriends)
{
NSString *c = [[friend objectForKey:#"name"] substringToIndex:1];
found = NO;
for (NSString *str in [self.sections allKeys])
{
if ([str isEqualToString:c])
{
found = YES;
}
}
if (!found)
{
[self.sections setValue:[[NSMutableArray alloc] init] forKey:c];
}
}
// Loop again and sort the friends into their respective keys
for (NSDictionary *friend in self.fbFriends)
{
[[self.sections objectForKey:[[friend objectForKey:#"name"] substringToIndex:1]] addObject:friend];
}
// Sort each section array
for (NSString *key in [self.sections allKeys])
{
[[self.sections objectForKey:key] sortUsingDescriptors:[NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:#"name" ascending:YES]]];
}
Here is my section setup and header views:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
if (tableView == self.searchDisplayController.searchResultsTableView) {
return 1;
} else {
return [[self.sections allKeys] count];
}
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
if (tableView == self.searchDisplayController.searchResultsTableView) {
return NSLocalizedString(#"FbFriendsSearchControllerSection", #"Fb Friends search controller - section title for search results table view");
} else {
return [[[self.sections allKeys] sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)] objectAtIndex:section];
}
}
Does anyone see how this is incorrect?
This is not a standard practice. There is no need for the extra section just to show a search.
Don't add the "search" section to your self.sections dictionary. Instead, you just have sections for the actual data. Then in your tableView:sectionForSectionIndexTitle:atIndex: method you can do:
- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index {
NSInteger res = 0;
if (tableView == self.tableView) {
if (index == 0) {
// If the user taps the search icon, scroll the table view to the top
res = -1;
[self.tableView setContentOffset:CGPointMake(0, 0) animated:NO];
} else {
res = ... // return the proper index for your data
}
} else {
res = 0;
}
return res;
}
Side note - you really want to have an array of dictionaries. The main array should represent the sections. As you have it now, you are constantly getting your main dictionary's keys and sorting them. This will be done MANY times - all needlessly. Using an array lets you quickly get the data from the section index.
I have three arrays. They are name, birthdates and remaining days like below:
name birthdate remaining
"Abhi Shah", "01/14", 300
"Akash Parikh", "12/09/1989", 264
"Anand Kapadiya", "12/01", 256
"Annabella Faith Perez", "03/02", 347
"Aysu Can", "04/14/1992", 25
"Chirag Pandya" "10/07/1987" 201
I want to rearrange the remaining days array into ascending order,
but at the same time name and birthdate should also get reordered in the same way.
See this example below:
name birthdate remaining
"Aysu Can", "04/14/1992", 25
"Chirag Pandya" "10/07/1987" 201
"etc..."
use NSMutableDictionary to store data to NSMutableArray
NSMutableArray *array=[[NSMutableArray alloc]init];
for (int i=0; i<totalRows; i++)
{
NSMutableDictionary *dic=[[NSMutableDictionary alloc]init];
[dic setValue:[array_1 objectAtIndex:i] forKey:#"names"];
[dic setValue:[array_2 objectAtIndex:i] forKey:#"birthdate "];
[dic setValue:[array_3 objectAtIndex:i] forKey:#"remanning"];
[array addObject:dic];
[dic release];
}
here after you arrange name array,use search option to use name not use index and
use NSPredicate search data in NSMutableArray
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"names matches[cd] %#", name];
NSArray *result = [array filteredArrayUsingPredicate:predicate];
NSMutableDictionary *dict = [[[result objectAtIndex:0] mutableCopy] autorelease];
NSLog(#"%#",dict);// result
self.arrayForRows = [[NSMutableArray alloc]init];
NSMutableArray *arrayForNames = [[NSMutableArray alloc]initWithObjects:#"Abhi Shah",#"Akash",#"Nagavendra",#"Ramana",#"Simhachalam", nil];
NSMutableArray *arrayForBirthDates = [[NSMutableArray alloc]initWithObjects:#"01/14/94",#"01/14",#"11/07/87",#"12/07/89",#"23/08/91", nil];
NSMutableArray *arrayForRemaining = [[NSMutableArray alloc]initWithObjects:#"200",#"320",#"32",#"450",#"14", nil];
for (int i=0; i<arrayForBirthDates.count; i++)
{
NSMutableDictionary *tempDicts = [[NSMutableDictionary alloc]init];
[tempDicts setObject:[arrayForNames objectAtIndex:i] forKey:#"names"];
[tempDicts setObject:[arrayForBirthDates objectAtIndex:i] forKey:#"birth"];
[tempDicts setObject:[NSNumber numberWithInt:[[arrayForRemaining objectAtIndex:i] intValue]] forKey:#"remaining"];
[self.arrayForRows addObject:tempDicts];
}
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"remaining" ascending:YES];
[self.arrayForRows sortUsingDescriptors:[NSArray arrayWithObject:sortDescriptor]];
Use this in tableView listing
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [self.arrayForRows count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifer = #"cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifer];
if (cell == nil)
{
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifer];
}
cell.textLabel.text = [[self.arrayForRows objectAtIndex:indexPath.row] valueForKey:#"names"];
return cell;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
once try like this it'l help you,
NSMutableDictionary *dict=[[NSMutableDictionary alloc]init];
[dict setObject:#"rahul" forKey:#"name"];
[dict setObject:#"10" forKey:#"value"];
NSMutableDictionary *dict1=[[NSMutableDictionary alloc]init];
[dict1 setObject:#"ttt" forKey:#"name"];
[dict1 setObject:#"6" forKey:#"value"];
NSMutableArray *ar=[[NSMutableArray alloc]init];
[ar addObject:dict];
[ar addObject:dict1];
NSSortDescriptor *Sorter = [[NSSortDescriptor alloc] initWithKey:#"name" ascending:NO];
[ar sortUsingDescriptors:[NSArray arrayWithObject:Sorter]];
NSLog(#"---%#",ar);
Put each row into a dictionary, and out those dictionaries into an array. Then sort your away using a predicate or sort block. If you want an array containing just the sorted names for example, you could use [ array valueForKeyPath:#"name" ]
Array looks like:
[
{ #"name" : ...,
#"birthdate" : ...birthdate...,
#"remaining" : ...days remaining... } ,
{...},
{...}
]
such as your MutableArray is a Dictionarys array , you can use sortUsingComparator
to sort the array
[array sortUsingComparator:^NSComparisonResult(id obj1, id obj2) {
int a = [(NSNumber *)[(NSDictionary *)obj1 objectiveForKey: #"remanning"] intValue];
int b = [(NSNumber *)[(NSDictionary *)obj2 objectiveForKey: #"remanning"] intValue];
if (a < b) {
return NSOrderedAscending;
}
else if(a == b)
{
return NSOrderedSame;
}
return NSOrderedDescending;
}];
For example , I have a test :
NSMutableArray *array = [[NSMutableArray alloc] initWithObjects:#(30),#(20),#(5),#(100), nil];
[array sortUsingComparator:^NSComparisonResult(id obj1, id obj2) {
int a = [(NSNumber *)obj1 intValue];
int b = [(NSNumber *)obj2 intValue];
if (a < b) {
return NSOrderedAscending;
}
else if(a == b)
{
return NSOrderedSame;
}
return NSOrderedDescending;
}];
[array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSLog(#"%# , %d",obj,idx);
}];
then the output is :