NSTable and NSMutableArray Sorting - objective-c

I have an NSTableView that I am trying to sort. The data source is an NSMutableArray which contains instances of custom classes.
-(void)tableView:(NSTableView *)tableView sortDescriptorsDidChange: (NSArray *)oldDescriptors
I am using the above so that I can track when the user presses a header of the table. I am using the following method to do the sorting:
NSSortDescriptor *sortDescriptor;
sortDescriptor = [[[NSSortDescriptor alloc] initWithKey:#"make" ascending:bool_asc_desc] autorelease];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
NSArray *sortedArray;
sortedArray = [ads_printers_array sortedArrayUsingDescriptors:sortDescriptors];
What I would like to know is how can I pass on the table column which was pressed? This would allow me to change the initWithKey:#"make" (using if statements) so that I can sort according to the header clicked.
Thanks.
P.S. I have tried the following:
if ([ads_rdp_driver_table selectedColumn] == tc_make)
with ads_rdp_driver_table being my NSTableView and tc_make being an NSTableColumn which I have defined. However, I am getting this error:
ISO C++ forbids comparison between pointer and integer
I guess this could work if I could figure out the error.

The selectedColumn method of an NSTableView returns the index of the selected column (not a pointer to it), which is why you are getting the compile error. To get the actual column, try something like this instead (code my contain errors, Im typing verbatim):
NSTableColumn * selectedColumn = (NSTableColumn *)[[tableView tableColumns] objectAtIndex: [tableView selectedColumn]];
NSLog(#"Column identifier: %#", [selectedColumn identifier]);

Related

Returned JSON data is changing from NSDictionary to NSArray

I'm having a little difficulty with a JSON service that I'm consuming and iterating over. When I consume the service I am looping over the data as you would expect because of the number of records.
I'm saving that loop'ed data into an NSArray which I use later in a UITableView. Next I'm simply allowing the user to tap the selected row (from the json data result) to show more detail. Pretty simple so far.
Every element from the JSON service is NSString. So far nothing tricky. However, one element within the NSArray after the service has been put into the NSObject is showing HEX code, see below.
altitude NSString * 0x7ff8d4cd3d30 0x00007ff8d4cd3d30
Of course the app has a meltdown because it can't figure out what HEX is when I'm using that NSArray object to display key elements i.e. altitude. Now the odd thing is every other element within the NSArray looks like this see below.
latitude __NSCFString * #"21.45852" 0x00007ff8d4ca54f0
I have read a few suggestions stating this is normal for NSString and JSON data. But not really how to fix it.
What I have found is that NSArray after the JSON is complete is changing just that one element. I have also tried changing it from an INT to an NSString however same result (I know its a NSString in the first place btw, I was just trying different ideas.)
Abstract of JSON Call and loop to add into NSArray object.
//Do something with returned array
dispatch_async(dispatch_get_main_queue(), ^{
NSDictionary *pilotJson = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];
//Loop through the JSON array
NSArray *currentPilotsArray = [pilotJson valueForKeyPath:#""];
//set up array and json call
pilotsArray = [[NSMutableArray alloc]init];
NSArray *keys=[pilotJson allKeys];
for (NSString *key in keys){
NSDictionary *elementDictionary=pilotJson[key];
NSString *altitude = elementDictionary[#"altitude"];
NSInteger n = [altitude intValue];
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
formatter.numberStyle = NSNumberFormatterDecimalStyle;
NSString *string = [formatter stringFromNumber:#(n)];
NSString *nAltitude = [NSString stringWithFormat:#"%# ft", string];
[pilotsArray addObject:[[LiveMap alloc]initWithaltitude:nAltitude ]];
.
.
.
So when I get to this point of the code where the user taps the relevant record I get a crash and the application aborts. I'm assuming this is from the above NSString vs __NSCFString
-(void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
//Pass the details the the detail view controller
PilotsFlightDetailViewController *detail = [self.storyboard instantiateViewControllerWithIdentifier:#"FlightDetails"];
NSLog(#"array: %#", pilotsArray);
NSString *AltitudeString = [[self.pilotsArray valueForKey:#"altitude"]objectAtIndex:indexPath.row]; <-----WIGS OUT HERE
I find this super odd as every other element works normally, but this one simply has issues. Any suggestions?
UPDATE:
NSLog of pilotsArray as per the request.
[1] LiveMap * 0x7f8b0265e1b0 0x00007f8b0265e1b0
altitude __NSCFString * #"21 ft" 0x00007f8b02664860
Also the jsonArray from the Service Directly.
Okay. I got it working. There was nothing wrong with the code. It however recognised another NSObject that had a same name "altitude" and for some reason it was getting mixed up.
I changed the name in the NSObject to something entirely unique and updated the instances in the relevant places. This did it. Lesson learnt always make sure you have named your variables appropriately.

Search for object in NSMutableArray and change value for that object

I have project where user can add some Items to TableView. My data source for this TableView are objects with 2 properties NSString * nameOfItem and NSNumber * numberOfItem. Is any possibility to check my NSMutableArray if it contain string #"someString" as property nameOfItem and if yes than change numberOfItem +1 ?
UPDATE:
I try to do it with for(...in...) but it works just when i have only one object in my NSMutableArray. If i have more objects there it create one new object and than change value to ++ on old one:
Here is some code what i tried:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
NSString*nameToCheck = [NSString stringWithFormat:#"%#", [self.ivc.objects objectAtIndex:indexPath.row]];
for (Items *itemNamed in self.ivc.shoppingList.items) {
if ([itemNamed.nameOfItem isEqualToString:nameToCheck]) {
[itemNamed setNumberOfItem:[NSNumber numberWithInt:[itemNamed.numberOfItem integerValue]+1.0]];
} else {
Items *item = [Items new];
[item setNameOfItem:name];
[item setNumberOfItem:#(1)];
[self.ivc.shoppingList.items insertObject:item atIndex:0];
}
I want to create new one only if is not yet in that list.
Any help appreciated.
I don't know something like this but you can use a NSDictionary instead of NSArray using the name property as a key. And if you don't want to do that, you can sort the array and make a binary search for element.

UITableView is not displaying Cell Style 2

I'm trying to display an array of dictionaries variable in the table Cell Style 2 which is what the Contacts App uses to display the information of the contact.
For some reason the cell style does not show up.
The array variable "arrayOfDictionaries_E" which has the following setup.
/// Dictionary 1
NSArray *dataArray = [NSArray arrayWithObjects: #"Host", hostname, nil];
NSArray *keysArray = [NSArray arrayWithObjects: #"Label", #"Data", nil];
NSDictionary *host_dict = [[NSDictionary alloc] initWithObjects: dataArray forKeys: keysArray];
/// Dictionary 2
NSArray *dataArray2 = [NSArray arrayWithObjects: #"IP", hostIP, nil];
NSArray *keysArray2 = [NSArray arrayWithObjects: #"Label", #"Data", nil];
NSDictionary *ip_dict = [[NSDictionary alloc] initWithObjects: dataArray2 forKeys: keysArray2];
// Storage into Array of Dictionaries
NSArray *dictionariesArray = [[NSArray alloc] initWithObjects: host_dict, ip_dict, nil];
I have tested that the array contains the 2 dictionaries and both dictionaries are populated.
So I'm having trouble with:
First, I want the all cells to use CellStyle2.
Second, I want each dictionary to be created in a new section. (like the the Contacts App, selectively bundle groups of items together)
So here comes the code in my implementation file. It is a UIViewController adopting the 2 UITableView protocols.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return [arrayOfDictionaries_E count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return 2;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue2 reuseIdentifier:CellIdentifier] autorelease];
}
NSDictionary * dict = [arrayOfDictionaries_E objectAtIndex:indexPath.row];
cell.textLabel.text = [dict objectForKey:#"label"];
cell.detailTextLabel.text = [dict objectForKey:#"Data"];
return cell;
}
Find a screenshot of the current tableView here.
On the nib, I have linked the tableView outlet to the file's Owner and done likewise for the delegate and source. I have changed the Style to Grouped.
However as seen on the image above, when I run the program the cell doesn't display in the proper style specified also I don't know how to get each row to display in separate sections. --> I'm think of an Array > Array > Dictionary variable, to get to work, however I'm quite lost at the fact of how this can be implemented.
I'm still relatively new to Objective-c, so you may need to explain with actual code implementations, to demonstrate how I may achieve this.
Thanks in advance to taking the time to answer this question.
There are 2 problems which I think could be in your way:
You are using #"label" as the key when populating the cell, but #"Label" when populating the dictionary. The keys are case sensitive
You are using indexPath.row, this will only be 0 or 1 since you have 2 rows per section. You should be using indexPath.section since there is one section for each entry in your array.
I'm not sure what you do expect to see in rows one and two, though.
OK, I've had another look at your question and you seem to be making things a bit more complicated than they need to be. You appear to have a host name and an IP address that you want to display in two cells within a section, with the same labels in each part of the section.
Why not just have a single array of dictionaries, holding the name and IP address, then in cellForRowAtIndexPath pick the dictionary based on the section number, then based on the row number display the relevant item from the dictionary, and hardcode the label at that point?
By the way, the reason your cell style does not appear is because you are passing nil for the label due to the issue mentioned above, so it appears to only have a single label.

Sorting a mutable array using a dictionary key

I am trying to create a simple mutable array with a single key ("dayCounter") that I intend to use for sorting. I've read loads of examples on line, but no joy.
So I create this array. Note the first entry is a NSDictionary object. (The other objects are text)
cumArray = [NSMutableArray arrayWithObjects:[NSMutableArray arrayWithObjects:
[NSDictionary dictionaryWithObject:[NSString stringWithFormat:#"%i", dayCounter] forKey:#"dayCounter"],[[dailyArray objectAtIndex:x]objectAtIndex:0],[[dailyArray objectAtIndex:x]objectAtIndex:1],[[dailyArray objectAtIndex:x]objectAtIndex:2], nil],nil];
I save the array in a plist and everything looks great after the load.
However, when I come to sort the array, the program crashes. I have tried every combination of the following:
NSSortDescriptor *aSortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"dayCounter" ascending:YES];
[cumArray sortUsingDescriptors:[NSArray arrayWithObject:aSortDescriptor]];
Do I need a dictionary item to act as a key? Can I sort on the first object any easier? Any help is much appreciated.
Sometimes using too many nested expressions can obscure what's really going on. For example, the 'simple' mutable array you created actually contains a nested mutable array, rather than directly containing the dictionaries you're trying to sort.
So instead of this:
cumArray = [NSMutableArray arrayWithObjects:[NSMutableArray arrayWithObjects:
[NSDictionary dictionaryWithObject:[NSString stringWithFormat:#"%i", dayCounter] forKey:#"dayCounter"],[[dailyArray objectAtIndex:x]objectAtIndex:0],[[dailyArray objectAtIndex:x]objectAtIndex:1],[[dailyArray objectAtIndex:x]objectAtIndex:2], nil],nil];
try doing this
NSDictionary *dict1 = [NSDictionary dictionaryWithObject:[NSString stringWithFormat:#"%i", dayCounter]
forKey:#"dayCounter"]
NSArray *objs = [dailyArray objectAtIndex:x];
NSDictionary *dict2 = [objs objectAtIndex:0];
NSDictionary *dict3 = [objs objectAtIndex:1];
NSDictionary *dict4 = [objs objectAtIndex:2];
// Note: You might want to temporarily log the values of dict2 - 4 here to make sure they're
// really dictionaries, and that they all actually contain the key 'dayCounter'.
cumArray = [NSMutableArray arrayWithObjects:dict1, dict2, dict3, dict4, nil];
Assuming that you really have a mutable array of dictionaries, each of which contains the key dayCounter, the sort descriptor you showed in your example should work just fine.
Your setup makes no sense. You are saying yourself that only the first object in the array is a dictionary that contains the key `#"dayCounter" ("The other objects are text"). How is it supposed to be sorted if only one object contains the sort criteria?
You need to sort the array with a method, like - (NSComparisunResult)compareDict
If you have to compare 2 dictionaries and determine which one should be ordered above the other ( NSOrderedAscending ) then you need to "extend" NSDictionary:
#interface NSDictionary (SortingAdditions) {}
- (NSComparisonResult)compareTo:(NSDictionary *)other;
#end
#implementation NSDictionary (SortingAddictions)
- (NSComparisonResult)compareTo:(NSDictionary *)other
{
if( [self count] > [other count] )
{ return NSOrderedAscending; }
}
#end
This method will sort NSDictionaries according to the amount of objects that they contain.
Other values you can return here are: NSOrderedDescending and NSOrderedSame.
Then you can sort the mutable array with:
[SomeMutableArray sortUsingSelector:#selector(compareTo:)];
Keep in mind that every object in the array will need to be an NSDictionary, otherwise you will get an exception: unrecognized selector sent to instance blabla
You can do the same thing for any type of object, if the array contains both NSStrings, NSNumbers and NSDictionaries you should take a different approach

How to append two NSMutableArray's in Iphone sdk or append an NSArray With NSMutableArray?

I need to append two NSMUtableArray's can any one suggest me how it possible?
My code is:
NSMutableArray *array1 = [appDelegate getTextList:1];
NSArray *array2 = [appDelegate getTextList:2];
[array1 addObjectsFromArray:array2];//I am getting exception here.
Anyone's help will be much appreciated.
Thanks all,
Lakshmi.
What's probably happening, is that your [appDelegate getTestList:1] is not actually returning a NSMutableArray, but a NSArray. Just typecasting the array as mutable by holding a pointer to it like that will not work in that case, instead use:
NSMutableArray *array1 = [[appDelegate getTextList:1] mutableCopy];
NSArray *array2 = [appDelegate getTextList:2];
[array1 addObjectsFromArray:array2];
Or you could store the 'textList' variable that you have in your appDelegate as an NSMutableArray in the first place. I am assuming that you have an NSArray of NSArrays (or their mutable versions). Eg.
// In the class interface
NSMutableArray *textLists;
// In the function in which you add lists to the array
NSMutableArray *newTextList;
[self populateArray:newTextList]; // Or something like that
[textLists addObject:newTextList];
Note: that you will probably have a different workflow, but I hope that you get the idea of storing the actual lists as NSMutableArrays.
Another Note: the second method WILL modify in place the NSMutableArray that [appDelegate getTextList:1]; returns
Try this:
NSMutableArray *result =
[[appDelegate getTextList:1] mutableCopy]
addObjectsFromArray:[appDelegate getTextList:2]];
You're getting the exception because you're trying to send mutating messages to an immutable array.