Sorting NSDictionary from JSON for UITableView - iphone

I am having trouble trying to sort my NSDictionary in order, and it's been weeks and I still haven't figured it out, so I wish some one could give me a hand here...
NSDictionary data is from JSON and the data is already sorted from the server and it displayed in order in the JSON, but when it retrieved and converted to NSDicitionary, the order is all wrong...
This is the JSON
{
"categories":{
"unknownCategoryName":[
{ "key1":"value1",
"key2":"value2"
}],
"unknownCategoryName1":[
{ "key1":"value1",
"key2":"value2"
}],
"unknownCategoryName2":[
{ "key1":"value1",
"key2":"value2"
}],
"unknownCategoryName3":[
{ "key1":"value1",
"key2":"value2"
}]
}
}
The category quantity and its name will not be known until the JSON is received, so this is what I am using to get the count and setting up the tableView and the section
NSDictionary *results = [jsonString JSONValue];
NSDictionary *all = [results objectForKey:#"categories"];
self.datasource = all;
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [self.datasource count];
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
NSString *title = [[self.datasource allKeys] objectAtIndex:section];
return title;
}
And what I want to do is to list out the section and the section title as the order in the JSON.... rather than a chaos display like
unknownCategory3
unknownCategory1
unknownCategory
unknownCategory2
The "unknownCategory" is Product Category name, they are not in alphabet order, without any number, and they are already sorted and display in order in the JSON...
So, it would be great if you guys could help.
Thanks in advance...

Keys are not ordered in NSDictionaries, so you have to sort the keys first before using them in your table view. So, instead of using [self.datasource allKeys] to get your section titles, first create a sorted array: sorted = [[self.datasource allKeys] sortedArrayUsingSelector:#selector(caseInsensitiveCompare:)], and then use that array to get the titles: title = [sorted objectAtIndex:section].
After Edit to answer a further question:
To use the sorted array to get the values you want into your table, I think this should be close. You would have to add a property, NSArray *sorted, to your .h file, and then this in your .m (this is assuming the structure of your json is as you posted above):
NSDictionary *results = [jsonString JSONValue];
NSDictionary *all = [results objectForKey:#"categories"];
self.datasource = all;
self.sorted = [[self.datasource allKeys] sortedArrayUsingSelector:#selector(caseInsensitiveCompare:)];
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return [self.sorted count];
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
NSString *title = [self.sorted objectAtIndex:section];
return title;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [[self.datasource valueForKey:[self.sorted objectAtIndex:section]]count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellIdentifier];
}
NSArray *theArray = [self.datasource valueForKey:[self.sorted objectAtIndex:indexPath.section]];
NSString *text1 = [[theArray objectAtIndex:0] valueForKey:#"key1"];
NSString *text2 = [[theArray objectAtIndex:0] valueForKey:#"key2"];
cell.textLabel.text = [NSString stringWithFormat:#"%#\r%#",text1,text2];
cell.textLabel.numberOfLines=0; // this set the number of lines to unlimited
return cell;
}
You didn't say how you wanted to display two different values in one table row -- in this example, I concatenated them into one string with a return between the two.

Seems a strange JSON. You could have used like this
{
"categories": [
"unknownCategoryName": [
{
"key1": "value1",
"key2": "value2"
}
],
"unknownCategoryName2": [
{
"key1": "value1",
"key2": "value2"
}
]
]
}
And it is very easy to sort if you did like above.
To sort JSON provided in your question
-(NSMutableArray *)getAllKeys:(NSDictionary *)jsonDictionary{
NSDictionary *all = [results objectForKey:#"categories"];
NSMutableArray *allKeys = [all allKeys];
NSSortDescriptor *sorter = [[NSSortDescriptor alloc] initWithKey:#"self" ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObject: sorter];
[allKeys sortUsingDescriptors:sortDescriptors];
[sorter release];
return allkeys;
}
-(void)createSortedDictionary{
NSMutableDictionary *dictionaryMutable=[NSMutableDictionary dictionary];
for(NSString *string in allkeys){
[dictionaryMutable setObject:[all objectForKey:string] forKey:string];
}
}
dictionaryMutable will hold the sorted dictionary

Did you write this JSON or are you getting it from somewhere that you can't edit? It looks malformed to me. categories should be an array and the current array placement makes no sense. If the JSON looked like this:
{
categories:[
{unknownCategoryName:
{ key1:value1,
key2:value2
}},
{unknownCategoryName2:
{ key1:value1,
key2:value2
}}
]
}
Then you could read categories into an NSArray, and your delegate methods would look like this (and display in proper order):
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [categoriesArray count];
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
NSDictionary *dict = [categoriesArray objectAtIndex:section];
NSString *title = [[dict allKeys] objectAtIndex:0];
return title;
}

I think the only way you can preserve the original order is to do the initial parsing yourself after creating a JSON string from the JSON data. If the structure of the JSON string is as you posted, you could use an NSScanner to find the strings between quote marks, and then select only the ones that have a [ after the final quotation mark to add to an array which will be your ordered array of keys. You can then create the NSDictionary using a JSON parser, and get the values by looping through your ordered keys and sending them valueForKey:.

Related

-[__NSCFDictionary objectAtIndex:]: unrecognized selector sent to instance 0x8943940 while parsing JSON Page to UITableView

I have been through many links and fortunately I got many similar links but nothing worked Out.
my array is appearing like this in Output, using NSLog()..
products = (
{
cid = 1;
name = "hello world";
},
{
cid = 2;
name = "It is an array";
},
{
cid = 3;
name = "error error";
},
{
cid = 4;
name = "OMG! still same";
},
{
cid = 5;
name = "any help";
...
...
},
{
cid = 130;
name = "How is the work going on.";
},
{
cid = 131;
name = "Had a nice lunch";
}
);
success = 1
Code to parse JSON, where "news" is an Array var..
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
[UIApplication sharedApplication].networkActivityIndicatorVisible=NO;
news = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
NSLog(#"Feeds...%#",news);
[myTableView reloadData];
}
Code to display table row, here in "news" value is being displayed by NSLog(); Error comes for NSDictionary.
-(UITableViewCell *)tableView: (UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
NSLog(#"=====%#====",news);
NSDictionary *myDict = [news objectAtIndex:indexPath.row];
NSArray *myArray = [myDict objectForKey:#"name"];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"MainCell"];
if (cell==nil) {
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"MainCell"];
}
for (NSString *str in myArray) {
cell.textLabel.text = str;
}
return cell;
}
I am not able to find what i'm missing here.., Please help me out.
Thank you in advance.
Your JSON structure is something like this:
Dictionary-->Array-->Object (Dictionary).
So you need to do:
Extract the Array from the dictionary using the key product
Extract an object(Dictionary) from the array using the index
Get the name from the dictionary object using the key name
Instead of this:
NSDictionary *myDict = [news objectAtIndex:indexPath.row];
NSArray *myArray = [myDict objectForKey:#"name"];
Use:
NSArray *myArr = [news objectForKey:#"products"];
cell.textLabel.text = [[myArr objectAtIndex:0] objectForKey:#"name"];
Edit
Also change your numberOfRowsInSection like:
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [[news objectForKey:#"products"] count];
}
news is a NSDictionary, and a dictionary does not have a method objectAtIndex:
You have to use instead [news objectForKey:...];
[news objectAtIndex:indexPath.row];
NSMutabledictionary is indexpath.row so add first news in array and that array name add in
your code and you get nothing an
Put NSDictionary in Array then use it in Table method
NSMutableArray *result=[[NSMutableArray alloc] init];
result = [googleResponse valueForKey: #"products"];
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSMutableDictionary *dt = [result objectAtIndex:indexPath.row];
}
It will Help you !!!!!

UITableView not reversing

I'm working on information retrieval where in the latest information in on top of UITableView. The code goes like this.
-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
LoanModel* loan = _feed.products[indexPath.row];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" ];
if (cell == nil) {
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleValue2 reuseIdentifier:#"Cell"];
}
NSString *myString = [NSString stringWithFormat:#"%#",[loan name]];
NSArray *myWords = [myString componentsSeparatedByString:#","];
NSMutableArray *reversed = [NSMutableArray arrayWithCapacity:[myWords count]];
NSEnumerator *enumerator = [myWords reverseObjectEnumerator];
for (id element in enumerator) {
[reversed addObject:element];
}
NSMutableString *reverseString = [reversed componentsJoinedByString:#","];
cell.textLabel.text = reverseString;
return cell;
}
I'm getting the JSON object which contain two fields cid and name respectively. I'm showing only the name is tableview. But I want to show it in reverse i,e last update should show at first. But above code showing the content in reverse. IF "hello" is the content, then it shows "olleh". Please suggest me how to get the UITableView in reverse list. I'm a newbie to iOS.
If you want to have the reversed order of cells / objects they represent, you have to change
LoanModel* loan = _feed.products[indexPath.row];
to
LoanModel* loan = _feed.products[_feed.products.count - 1 - indexPath.row];
assuming that _feed.products is an NSArray.
The array you are using to display the the data in tableView, you'll need to reverse that and then display the data in tableView
NSArray* reversed = [[myArray reverseObjectEnumerator] allObjects];
you can use this to make your array objects order in reverse.
Try This:
NSArray* reversedArray = [[myWords reverseObjectEnumerator] allObjects];

iPhone SDK: How To Create Sections On UITableView

I have this data that I need to put inside my UITableView but I'm getting confused on how to properly implement it. I cannot properly separate the values to segregate the Boys data to the Girls data.
{
"QUERY": {
"COLUMNS": [
"NAME",
"GENDER"
],
"DATA": [
[
"Anne",
"Girl"
],
[
"Alex",
"Boy"
],
[
"Vince",
"Boy"
],
[
"Jack",
"Boy"
],
[
"Shiela",
"Girl"
],
[
"Stacy",
"Girl"
]
]
},
"TOTALROWCOUNT": 6
}
I have this codes:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [genderArray count];
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
return [genderArray objectAtIndex:section];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [namesArray count];
}
namesArray has all the values returned by NAME while genderArray has all the values of GENDER. I'm getting confused.
AS you are getting confused, break your data into pieces. You want two arrays, one per section. So you want one array of boys names, and another array of girls names.
You can obtain this by iterating through the embedded DATA array.
Turn your data into an NSDictionary object. Your data looks like JSON so...
NSDictionary* myDict = [NSJSONSerialization JSONObjectWithData:myJsonData
options:0 error:&error];
Extract the data...
NSArray* dataArray = [myDict objectForKey:#"DATA"];
Iterate...
NSMutableArray* boys = [[NSMutableArray alloc] init];
NSMutableArray* girls = [[NSMutableArray alloc] init];
for (id person in dataArray) {
if ([[person objectAtIndex:1] isEqualToString:#"Girl"])
[girls addObject:[person objectAtIndex:0]];
else [boys addObject:[person objectAtIndex:0]];
}
Now you have two arrays, one for each of your table sections. Make an array of sections, and put these arrays into it:
NSArray* sections = [NSArray arrayWithObjects:boys,girls,nil];
Make a separate array for your section headers:
NSArray* headers = [NSArray arrayWithObjects:#"Boys",#"Girls",nil];
Now your data source methods look like this:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [sections count];
}
- (NSString *)tableView:(UITableView *)tableView
titleForHeaderInSection:(NSInteger)section
{
return [headers objectAtIndex:section];
}
- (NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section
{
return [[sections objectAtIndex:section] count];
}
and finally
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
...
cell.textLabel.text = (NSString*)[[self.sections objectAtIndex:indexPath.section]
objectAtIndex:indexPath.row];

Parsing json string in iOS Objective C

I am trying to build my first iOS app that parses json from a web service.
In my .h file i create an array
NSArray *items;
In my .m file i call the website and store the data into the array. This all goes fine except eventually I am trying to display the data in a uitableview.
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"ItemsCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
NSLog(#"%#",items);
NSDictionary *item = [items objectAtIndex:indexPath.row];
NSString *title = [item objectForKey:#"title"];
cell.title.text = title;
return cell;
}
The nslog of items produces:
{
category = code;
keyword = "";
limit = 20;
lowPrice = "";
page = 0;
products = (
{
description = "...";
mobileFriendly = 1;
popularity = 2021;
productId = code;
title = "title";
}, and so on...
)
I am getting an error trying to do NSDictionary *item = [items objectAtIndex:indexPath.row]; It says doing objectAtIndex:indexPath.row is invalid.
What am I doing wrong?
Thanks for your help!
The value stored in items is an instance of NSDictionary, but you're sending it an NSArray message. Also, the code you posted is referring to gift and ignoring the item anyway.
The array you're looking for is actually stored under the key products inside the items dictionary, so you need to do something like this:
NSArray *products = [items objectForKey:#"products"];
NSDictionary *product = [products objectAtIndex:indexPath.row];
// Then use the product, instead of using 'gift' (whatever that is).
NSString *title = [product objectForKey:#"title"];
items is not a NSArray - it is a NSDictionary. so it has no objectAtIndexPath: method.

Static Table View

I am creating a static table view (must be compatible with iOS 4 - so I can't use iOS 5's method).
The way I have it is that I have two sections; the first has one cell, and the second has two cells. I made two arrays, one with the title of the only cell in the first section, and the second with both titles for both cells in the second section. So my dictionary looks like this:
(NSDictionary *) {
First = (
Title1 < --- Array (1 item)
);
Second = (
"Title1", < --- Array (2 items)
Title2
);
}
The issue I have is that I need to return number of rows in a section using tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section. So my question is, how do I retrieve the section from the dictionary using NSInteger section? I would also have to do the same thing in tableView:cellForRowAtIndexPath.
Thank you
If you don't understand how dictionaries work, I'd recommend simplifying the problem. Create one array for each section, then inside your delegate methods use a switch() statement to call [array count] for the row count etc. For the section count you could still use the original dictionary with [[dictionary allKeys] count].
EDIT:
I just saw #fzwo recommends the same thing in two comments
Your best bet is an array of arrays, as has been mentioned. To avoid the complexity with dictionaries, create two NSArray ivars for the table data and the section titles.
// in viewDidLoad
tableData = [NSArray arrayWithObjects:
[NSArray arrayWithObjects:
#"Row one title",
#"Row two title",
nil],
[NSArray arrayWithObjects:
#"Row one title",
#"Row two title",
#"Row three title",
nil],
nil];
sectionTitles = [NSArray arrayWithObjects:
#"Section one title",
#"Section two title",
nil];
// in numberOfSections:
return tableData.count;
// in numberOfRowsInSection:
return [[tableData objectAtIndex:section] count];
// in titleForHeaderInSection:
return [sectionTitles objectAtIndex:section];
// in cellForRowAtIndexPath:
...
cell.textLabel.text = [[tableData objectAtIndex:indexPath.section]
objectAtIndex:indexPath.row];
You could use other objects instead of the row titles if you need more data available to your cell.
To get the number of rows in your section you can use the following:
tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSString *key = [[dictionary allKeys] objectAtIndex: section];
return [[dictionary objectForKey:key] count];
}
And to get the cell value:
tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *key = [[dictionary allKeys] objectAtIndex: indexPath.section];
NSArray *values = [dictionary objectForKey:key];
NSString *value = [values objectAtIndex: indexPath.row];
// code to create a cell
return cell;
}