I want to use a variable-sized multi-dimensional array in my app to save data. The data structure I want to use is as below, the first element in each row is followed by corresponding multiple values.
array = { {a, a_val1, a_val2, a_val3}.
{b, b_val1},
{c, c_val1, c_val2, c_val3, c_val4, c_val5}
}
Any idea how I can implement in objective-c?
use NSMutableArray like so
NSMutableArray *curRow; /* use to access the row while loading with objects */
NSMutableArray *array = [[NSMutableArray alloc] init]; /* your main multidim array */
curRow = [NSMutableArray array];
[curRow addObject:/* what you want here */];
[curRow addObject:/* what you want here */];
[curRow addObject:/* what you want here */];
[array addObject:curRow]; /* first row is added */
/* rinse and repeat */
curRow = [NSMutableArray array];
[curRow addObject:/* what you want here */];
[curRow addObject:/* what you want here */];
[curRow addObject:/* what you want here */];
[array addObject:curRow];
use NSMutableArray
Below is the example for your understanding ...
NSMutableArray * multiArray = [[NSMutableArray alloc] initWithCapacity:5];
[multiArray addObject:[NSArray arrayWithObjects:a,a_val1,a_val2]];
[multiArray addObject:[NSArray arrayWithObjects:a,a_val1,a_val2,a_val3,a_val4]];
[multiArray addObject:[NSArray arrayWithObjects:a,a_val1,a_val5]];
[multiArray addObject:[NSArray arrayWithObjects:a,a_val1,a_val2,a_val3,a_val4,a_val5,a_val6]];
And Don't forget to release to multiArray array because we have alloced it ...
Objective-C does not have a real 2 dimensional array type but you can implement it with the
following codes..
in your header file --- yourheader.h
#import <Foundation/Foundation.h>
#interface yourheader : NSObject{
NSMutableDictionary* DictionaryArrayType;
NSMutableArray* MultiArrayType;
NSArray* CaptionTitle;
NSArray* ObjectValue;
}
#property (strong, nonatomic) NSMutableDictionary* DictionaryArrayType;
#property (strong, nonatomic) NSArray* CaptionTitle;
#property (strong, nonatomic) NSArray* ObjectValue;
#property (strong, nonatomic) NSMutableArray* MultiArrayType;
-(id) AddArrayObjects:(NSString*)_Name : (NSString*)_Surname :(NSString*)_Age;
-(id) AddArrayDictionaryObject:(NSArray*)_ArrayObject : (NSArray*)_ArrayKey;
-(id) AddMultiArrayType:(id)_ArrayObject;
-(void) ShowMultiArrayType:(id)_ArrayObject;
#end
Now add to your objective-c file ---- objective-c.m
#import "yourheader.h"
#implimentation yourheader
#synthesize DictionaryArrayType;
#synthesize CaptionTitle;
#synthesize ObjectValue;
#synthesize MultiArrayType;
-(id)init {
if(self = [super init]){
NSString* const NAME = #"NAME";
NSString* const SURNAME = #"SURNAME";
NSString* const AGE = #"AGE";
//Adding fixed content to CaptionTitle Array
[self setCaptionTitle:[NSArray arrayWithObjects:NAME, SURNAME, AGE, nil]];
//add values to ObjectValue array
[self AddArrayObjects:#"Bob" :#"Obi" :#"200"];
//add values to dictionary
[self AddDictionaryArrayType:ObjectValue :CaptionTitle];
//Add to the Multi dimensional array [][]
[self AddMultiArrayType:DictionaryArrayType];
//add the second row values to ObjectValue array
[self AddArrayObjects:#"Barack" :#"Obama" :#"50"];
//add values to dictionary
[self AddDictionaryArrayType:ObjectValue :CaptionTitle];
//Add to the Multi dimensional array [][]
[self AddMultiArrayType:DictionaryArrayType];
//display the 2d Array
[self ShowMultiArrayType:MultiArrayType];
}
return self;
}
-(id)AddArrayObjects:(NSString *)_name :(NSString *)_surname :(NSString *)_age {
//Set the Array Objects;
[self setObjectValue:[NSArray arrayWithObjects:_name, _surname, _age, nil]];
return self;
}
-(id)AddDictionaryArrayType:(NSArray *)_ArrayObject :(NSArray*)_ArrayKey {
if(!DictionaryArrayType) {
//initialize disctionary
[self setDictionaryArrayType:[NSMutableDictionary dictionary]];
}
//add array obeject and Fixed Key decleared in CaptionTitle array
DictionaryArrayType = [NSMutableDictionary dictionaryWithObjects:_ArrayObject forKeys:_ArrayKey];
return self;
}
-(id) AddMultiArrayType:(id)_ArrayObject {
if(!MultiArrayType) {
[self setMultiArrayType:[NSMutableArray array]];
}
[MultiArrayType addObject:_ArrayObject];
return self;
}
-(void)ShowMultiArrayType:_ArrayObject {
for(id objects in _ArrayObject ) {
for(id key in objects) {
NSLog(#"%# key = : object = %#", key, [objects objectForKey:key]);
}
}
}
#end;
To finish add this to your appdelegate.m file inside the app
yourclassname* _yourclasspointer = [[yourclassname alloc] init];
[_youclasspointer ShowMultiArrayType:[_yourclasspointer MultiArrayType]];
You should see it in you console.
Related
My array is not saving the values I put in it...
I am defining my nsmutablearray *arrayClientList in .h file
#interface StartupTableViewController : UIViewController<UITableViewDataSource, UITableViewDelegate>
#property (strong, nonatomic) IBOutlet UITableView *tableView;
#property NSMutableArray *arrayClientList;
#property BOOL boolAddToClient;
//#property (strong, nonatomic) NSMutableArray *arrayAddClient;
#end
in .m file I am initializing like so
- (void)viewDidLoad {
[super viewDidLoad];
//initialize variables
self.arrayClientList = [[NSMutableArray alloc] init];
arraySelectedInformation = [[NSMutableArray alloc] init];
self.boolAddToClient = NO;
NSString *tstring = #"hello";
[self.arrayClientList addObject:tstring];
but then once I get to another method in this same class... the array is nil again. I must be doing something stupid for the array not to hold the values
-(void)viewDidAppear:(BOOL)animated{
//NSLog(#"appeared");
if (self.boolAddToClient) {
NSLog(#"add client to list");
self.boolAddToClient = NO;
[self.tableView reloadData];
}
else{
NSLog(#"startup");
}
}
I am trying to use it in another class
- (IBAction)buttonSubmit:(id)sender {
NSString *userDescription = [[NSString alloc] init];
NSString *userUsername = [[NSString alloc] init];
NSString *userPassword = [[NSString alloc] init];
userDescription = self.textfieldDescription.text;
userUsername = self.textfieldUserID.text;
userPassword = self.textfieldPW.text;
//check to make sure user filled out all fields
if (![userDescription isEqual:#""] && ![userUsername isEqual:#""] && ![userPassword isEqual: #""]){
NSLog(#"correct");
NSArray *arrayVC = self.navigationController.viewControllers;
StartupTableViewController *parentViewController = [arrayVC objectAtIndex:0];
parentViewController.boolAddToClient = YES;
NSMutableArray *arrayNewObjects = [[NSMutableArray alloc] initWithObjects:userDescription, userUsername, userPassword, nil];
NSMutableArray *tarray = parentViewController.arrayClientList;
[tarray addObject:arrayNewObjects];
[parentViewController.arrayClientList addObject:arrayNewObjects];
[self.navigationController popViewControllerAnimated:YES];
}
else{
NSLog(#"something missing");
}
}
Since I can't comment without rep, I must try with answer.
Try this:
In ViewDidLoad do alloc init with Strings you create in implementation and also change if block to this:
#implementation
{
NSString *userDescription;
NSString *userUsername;
NSString *userPassword;
}
-(void)viewDidLoad {
[super viewDidLoad];
NSString *userDescription = [[NSString alloc] init];
NSString *userUsername = [[NSString alloc] init];
NSString *userPassword = [[NSString alloc] init];
}
- (IBAction)buttonSubmit:(id)sender {
if (self.textfieldDescription.text.lenght != 0 && self.textfieldUserID.text.lenght != 0 && self.textfieldPW.text.lenght != 0) {
userDescription = self.textfieldDescription.text;
userUsername = self.textfieldUserID.text;
userPassword = self.textfieldPW.text;
....... and the rest
}
Please comment if it's not working, and I also think that you're not passing the informations right. Try searching an answer on how to pass arrays between TableViewControllers. Good Luck!
I have a non doc-based app with table view with two columns, whose Value bindings are bound to two properties of the arrangedObjects of an array controller. I have a NSMutableArray in my AppDelegate that hold the data, as well as the corresponding #property and #synthesize. I implement key-value-coding accessor methods and use those when inserting data to the array in order to generate KVO notifications. I fill up my array with some NSDictionary objects from xml file. I've got all that working (modify, remove and add items), but what I'd like to do is write it back to xml file.
xmlData writeToFile:fileName atomically:YES
is writing to disk, but don't reflect changes when editing table view. There must be some tiny little thing I am forgetting. What is it?
Any suggestions would be very nice!
The code:
// AppDelegate.h
#import <Cocoa/Cocoa.h>
#interface AppDelegate : NSObject <NSApplicationDelegate>{
NSXMLDocument *xmlDoc;
NSXMLElement *root;
NSMutableArray *children;
}
#property (assign) IBOutlet NSWindow *window;
#property (nonatomic, copy)NSArray *children;
- (void)setChildren:(NSArray *)newChildren;
- (NSUInteger)countOfChildren;
- (id)objectInChildrenAtIndex:(NSUInteger)index;
- (void)insertObject:(id)object inChildrenAtIndex:(NSUInteger)index;
- (void)removeObjectFromChildrenAtIndex:(NSUInteger)index;
- (IBAction)addObject:(id)sender;
- (IBAction)saveXMLToFile:(id)sender;
- (void) populateTable;
#end
// AppDelegate.m
#import "AppDelegate.h"
#implementation AppDelegate
-(void) addObjectToChildren:(id)object{
[self insertObject:object inChildrenAtIndex:[self countOfChildren]];
}
#synthesize children;
- (id)init{
self = [super init];
if (self) {
children = [[NSMutableArray alloc] init];
}
return self;
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification{
[self populateTable];
}
#pragma mark - Indexed Accessors
- (void)setChildren:(NSArray *)newChildren{
if (children!=newChildren) {
children=[newChildren mutableCopy];
}
}
-(NSUInteger)countOfChildren{
return [self.children count];
}
-(id)objectInChildrenAtIndex:(NSUInteger)index{
return [self.children objectAtIndex:index];
}
- (void)insertObject:(id)object inChildrenAtIndex:(NSUInteger)index{
[children insertObject:object atIndex:index];
}
-(void)removeObjectFromChildrenAtIndex:(NSUInteger)index{
[children removeObjectAtIndex:index];
}
#pragma mark -
-(void)populateTable{
NSError *error=nil;
NSString *pathname = [#"~/myFile.xml" stringByExpandingTildeInPath];
NSURL *url = [NSURL fileURLWithPath:pathname];
xmlDoc = [[NSXMLDocument alloc] initWithContentsOfURL:url options:NSXMLDocumentTidyXML error:&error];
root = [xmlDoc rootElement];
NSArray *array=[root nodesForXPath:#"number" error:&error];
NSMutableArray *mArray=[[NSMutableArray alloc]init];
for (NSXMLElement *element in array) {
NSMutableDictionary *dict=[NSMutableDictionary dictionaryWithObjectsAndKeys:[element stringValue],#"id",#"number", #"name", nil];
[mArray addObject:dict];
}
[self setChildren:[NSArray arrayWithArray:mArray]];
}
#pragma mark - Actions
- (IBAction)addObject:(id)sender {
NSMutableDictionary *dict=[NSMutableDictionary dictionaryWithObjectsAndKeys:#"0",#"id",#"number", #"name", nil];
NSXMLElement *element=[[NSXMLElement alloc]initWithName:[dict objectForKey:#"name"] stringValue:[dict objectForKey:#"id"]];
[root insertChild:element atIndex:[self countOfChildren]];
[self addObjectToChildren:dict];
}
- (IBAction)saveXMLToFile:(id)sender {
NSString *fileName = [#"~/myFile.xml" stringByExpandingTildeInPath];
NSData *xmlData = [xmlDoc XMLDataWithOptions:NSXMLNodePrettyPrint];
if (![xmlData writeToFile:fileName atomically:YES]) {
NSBeep();
NSLog(#"Could not write document out...");
}
}
#end
The xml file:
<root>
<number>0</number>
<number>1</number>
<number>2</number>
<number>3</number>
<number>4</number>
<number>5</number>
</root>
When you change values in the tableview the underlying array is updated but not your XML document. You need to update your XML document or recreate it from the array to hold edited values.
I am working on getting a list of songs to sort by artist. I have my code working and can print the songs and artists. Now I want to make it sort and print the list by artist. This is my first attempt at sorting and I know I have to be missing something simple here. I get -- Expected ')'-- and -- use of undeclared identifier 'listSongs' -- This is all in my #implementation file.
Any help is appreciated thank you in advance.
Here is my interface section
#interface Library : NSObject
#property (nonatomic, strong) NSMutableArray *list;
-(id) initList;
-(void) addSong: (Song *) song;
-(void) listSongs;
#end
This is my implementation section
#import "Library.h"
#implementation Library
#synthesize list;
-(id) initList
{
self = [super init];
if (self){
list = [NSMutableArray array];
}
return self;
}
-(id) init
{
return [self initList];
}
-(void) addSong:(Song *)song
{
[list addObject: song];
}
-(void) listSongs
{
NSLog(#"The list of my songs");
NSSortDescriptor *sorter;
sorter = [[[NSSortDescriptor alloc] initWithKey:#"artist" ascending: YES]
NSArray *sortDescriptors = [NSArray arrayWithObjects:sorter]; **// ** Expected ')'**
[listSongs sortUsingDescriptors:sortDescriptors]; //**use of undeclared identifier 'listSongs'
for (Song *song in list)
NSLog(#"%-20s by %s", [song.Title UTF8String], [song.Artist UTF8String]);
}
You should use NSArray *sortedArray = [listSongs arraySortedUsingDescriptors:sortDescriptors];
Basically this method returns a sorted array, it is called on an unsorted array and is given an array of sort descriptors.
As I understand from your code, what you're trying to do is to sort list which is declared property.
You need to replace this line
[listSongs sortUsingDescriptors:sortDescriptors];
With
self.list = [self.list sortUsingDescriptors:sortDescriptors];
Alright guys, I'm quite confused. So, I have an NSDictionary which is populated by a JSON string which looks like:
{"Success":true,"Devices":[{"UDId":"...","User":"...","Latitude":0.0,"Longitude":0.0}]}
Now, I know how to check if Success is true, but I need to loop through the array of Devices (JSON object) and create an internal array of Devices (internal app object) and I have no idea how to do that. Can someone please explain how to do it?
Here's my Device.m/h:
#import <CoreLocation/CoreLocation.h>
#import <Foundation/Foundation.h>
#interface Device : NSObject {
NSString *udId;
NSString *name;
NSNumber *latitude;
NSNumber *longitude;
}
#property (nonatomic, retain) NSString *udId;
#property (nonatomic, retain) NSString *name;
#property (nonatomic, retain) NSNumber *latitude;
#property (nonatomic, retain) NSNumber *longitude;
#pragma mark -
#pragma mark MKAnnotation Properties
#property (nonatomic, readonly) CLLocationCoordinate2D coordinate;
#end
----
#import "Device.h"
#implementation Device
#synthesize udId, name, latitude, longitude;
- (CLLocationCoordinate2D)coordinate {
CLLocationCoordinate2D internalCoordinate;
internalCoordinate.latitude = [self.latitude doubleValue];
internalCoordinate.longitude = [self.longitude doubleValue];
return internalCoordinate;
}
- (void)dealloc {
[udId release];
udId = nil;
[name release];
name = nil;
[latitude release];
latitude = nil;
[longitude release];
longitude = nil;
[super dealloc];
}
#end
And here's the methods where I should be reading the response and converting it to objects I can use:
- (void)requestFinished:(ASIHTTPRequest *)request {
if (![request error]) {
NSError *jsonError = nil;
NSDictionary *jsonDictionary = [NSDictionary dictionaryWithJSONString:[request responseString] error:&jsonError];
if (!jsonError || ([[jsonDictionary objectForKey:#"Success"] intValue] == 1)) {
// READ "DEVICES" AND CONVERT TO OBJECTS
} else {
// AUTHORIZATION FAILED
}
}
}
I'd really appreciate some help on this. I just can't seem to wrap my head around it...
Thanks in advance!
You are almost there. In your code where you say:
// READ "DEVICES" AND CONVERT TO OBJECTS
do this:
NSArray * devices = [jsonDictionary objectForKey:#"Devices"];
for(NSDictionary * deviceInfo in devices) {
Device * d = [[[Device alloc] init] autorelease];
[d setLatitude:[deviceInfo objectForKey:#"Latitude"]];
[d setLongitude:[deviceInfo objectForKey:#"Longitude"]];
[d setName:[deviceInfo objectForKey:#"User"]];
[d setUdId:[deviceInfo objectForKey:#"UDId"]];
// do some stuff with d
}
What's going on here: I didn't see what JSON library you are using to convert, but presuming it works like TouchJSON or SBJSON, the JSON array is automatically turned into an NSArray instance, while the inner hashes of the NSArray are NSDictionary objects. At the point that you have deserialized that JSON string, everything you're dealing with will be instances of NSString, NSNumber, NSArray and NSDictionary (and depending on the library, NSNull to represent null values).
First you need to define your initializer/constructor for your Device class.
Device.h
- (id)initWithUdid:(NSString *)udid name:(NSString *)name latitude:(NSNumber *)lat longitude:(NSNumber *)lon;
Device.m
- (id)initWithUdid:(NSString *)udid name:(NSString *)name latitude:(NSNumber *)lat longitude:(NSNumber *)lon {
if (self = [super init]) {
self.udid = udid;
self.name = name;
self.latitude = lat;
self.longitude = lon;
}
return self;
}
Then you can initialize a new object like:
Device *dev = [[Device alloc] initWithUdid:#"a udid" name:#"the name" latitude:latNum longitude:lonNum];
So, you should be able to iterate the array and build your Device objects like so:
NSArray *devicesArray = [dict objectForKey:#"Devices"];
for (NSDictionary *d in devicesArray) {
Device *dev = [[Device alloc] initWithUdid:[d objectForKey:#"UDId"]
name:[d objectForKey:#"User"]
latitude:[NSNumber numberWithDouble:[d objectForKey:#"Latitude"]]
longitude:[NSNumber numberWithDouble:[d objectForKey:#"Latitude"]]];
}
You want to access the array of device dictionaries from the top-level dictionary just as you did the Success value. Then iterating over the dictionaries you can use each's -keyEnumerator method to iterate over its keys.
- (void)requestFinished:(ASIHTTPRequest *)request {
if (![request error]) {
NSError *jsonError = nil;
NSDictionary *jsonDictionary = [NSDictionary dictionaryWithJSONString:[request responseString] error:&jsonError];
if (!jsonError || ([[jsonDictionary objectForKey:#"Success"] intValue] == 1)) {
NSArray* deviceArray = [jsonDictionary objectForKey:#"Devices"];
for(NSDictionary* dict in deviceArray)
{
for(NSString* key in [dict keyEnumerator])
{
NSLog(#"%# -> %#", key, [dict objectForKey:key]);
}
}
// READ "DEVICES" AND CONVERT TO OBJECTS
} else {
// AUTHORIZATION FAILED
}
}
}
Sounds like you need to reuse your line:
[jsonDictionary objectForKey:#"Success"]
try having a look at
[jsonDictionary objectForKey:#"Devices"]
You really need to figure out what type it returns.
If you're lucky, it returns an NSDictionary, or alternately something that you can easily turn into an NSDictionary.
Given a basic key/value array, I'm wanting to store two sorted arrays based on the original array: one array will be sorted by name, and the other by age.
The arrays seem to be sorting correctly when I output them to the log; however, when I try to access them elsewhere in the code, I'm receiving a EXC_BAD_ACCESS error.
Here's what I have so far:
// MyController.h
#interface MyController : UIViewController {
NSMutableArray *originalArray;
NSMutableArray *nameArray;
NSMutableArray *ageArray;
}
#property (nonatomic, retain) NSMutableArray *originalArray;
#property (nonatomic, retain) NSMutableArray *nameArray;
#property (nonatomic, retain) NSMutableArray *ageArray;
-(void)someRandomMethod;
#end
// MyController.m
#import "MyController.h"
#implementation MyController
#synthesize originalArray;
#synthesize nameArray;
#synthesize ageArray;
-(void)viewDidLoad {
// originalArray = (
// {
// "name" = "Sally";
// "age" = 18;
// },
// {
// "name" = "Chad";
// "age" = 26;
// },
// {
// "name" = "Carla";
// "age" = 24;
// },
// )
// sort by name
NSSortDescriptor *sortByNameDescriptor;
sortByNameDescriptor = [[[NSSortDescriptor alloc]
initWithKey:#"name"
ascending:NO] autorelease];
NSArray *sortByNameDescriptors = [NSArray arrayWithObject:sortByNameDescriptor];
nameArray = [originalArray sortedArrayUsingDescriptors:sortByNameDescriptors];
// sort by age
NSSortDescriptor *sortByAgeDescriptor;
sortByAgeDescriptor = [[[NSSortDescriptor alloc]
initWithKey:#"age"
ascending:NO] autorelease];
NSArray *sortAgeDescriptors = [NSArray arrayWithObject:sortByAgeDescriptor];
ageArray = [originalArray sortedArrayUsingDescriptors:sortByAgeDescriptors];
[super viewDidLoad];
}
-(void)someRandomMethod {
// whenever I try to access the sorted arrays, I receive the EXC_BAD_ACCESS error
[[nameArray objectAtIndex:0] valueForKey:#"name"];
[[ageArray objectAtIndex:0] valueForKey:#"age"];
}
-(void)viewDidUnload {
self.originalArray = nil;
self.nameArray = nil;
self.ageArray = nil;
[super viewDidUnload];
}
- (void)dealloc {
[originalArray release];
[nameArray release];
[ageArray release];
[super dealloc];
}
#end
Any ideas?
UPDATE: Thanks to #robin, by changing the code above to the code below, everything works great:
// sort by name
NSSortDescriptor *sortByNameDescriptor;
sortByNameDescriptor = [[[NSSortDescriptor alloc]
initWithKey:#"name"
ascending:NO] autorelease];
NSArray *sortByNameDescriptors = [NSArray arrayWithObject:sortByNameDescriptor];
nameArray = [[NSMutableArray alloc] initWithArray:[originalArray sortedArrayUsingDescriptors:sortByNameDescriptors]];
// sort by age
NSSortDescriptor *sortByAgeDescriptor;
sortByAgeDescriptor = [[[NSSortDescriptor alloc]
initWithKey:#"age"
ascending:NO] autorelease];
NSArray *sortAgeDescriptors = [NSArray arrayWithObject:sortByAgeDescriptor];
ageArray = [[NSMutableArray alloc] initWithArray:[originalArray sortedArrayUsingDescriptors:sortByAgeDescriptors]];
I dont think you know about this or not but when ever you create an object like string or array or dictionary, with init methods then the retain count gets incremented by 1
and if you create them like this
NSArray *anarray = [NSArray arrayWithArray:temp];
this will create an autorelease objects that will be released automatically after sometime.
So my advice don't use this type of code if you want to use the objects in more than 1 function. Always use init methods first to get the work done.
and if you are sure that the objects are not needed for the rest of the program than release them using release methode.