Objective-C: Parse string to object - objective-c

I have a simple object. It has several NSString properties (propertyA, propertyB, propertyC).
I have a string (read from a csv file) in the following form:
this is value A, this is value B, this is value C
another row A, another row B
Notice that the second row is missing the last property.
I want to parse the string into my object. Currently I'm grabbing a line from the csv file and then doing this:
MyObject *something = [[MyObject alloc] init];
NSArray *split = [line componentsSeparatedByString:#","];
if (something.count > 0)
something.propertyA = [split objectAtIndex:0];
if (something.count > 1)
something.propertyB = [split objectAtIndex:1];
if (something.count > 2)
something.propertyC = [split objectAtIndex:2];
This works well, but feels really horrible and hacky!
Has anyone got any suggestions for how I can improve the code?

Take a look at this tread about parsing CSV Where can I find a CSV to NSArray parser for Objective-C?
Dave DeLong wrote a CSV-parser library, you can find it here: https://github.com/davedelong/CHCSVParser
Hope this helps :)

Here's a CSV parsing extension to NSString that I used in the past to handle CSV data.
http://www.macresearch.org/cocoa-scientists-part-xxvi-parsing-csv-data
If basically adds a -(NSArray *)csvRows method to NSString that returns a NSArray with each row on your data and a NSArray inside each row to handle the columns.
It's the simplest and cleanest way I found so far to deal with the ocasional CSV data that comes up.

Your approach actually seems pretty sound, given the input format of the file, and assuming that no individual items actually contain a comma within themselves. As others have mentioned, CSV and/or custom flat files require custom solutions to get what you want from them.
If the approach above gets you the data you want, then I say use it. If it doesn't though, perhaps you can share the actual problem you're experiencing (ie, what data are you getting out, and what were you expecting?)

Consider using an array of keys that correspond to MyObject property names. For example:
NSString *propertyNames[] = { #"property1", #"property2", #"property3" };
NSArray *values = [line componentsSeparatedByString:#","];
NSArray *keys = [NSArray arrayWithObjects:propertyNames count:[values count]];
NSDictionary *dict = [NSDictionary dictionaryWithObjects:values forKeys:keys];
MyObject obj = [[MyObject alloc] init];
[obj setValuesForKeysWithDictionary:dict];
You could then consider adding an initWithDictionary: method to MyObject that calls setValuesForKeysWithDictionary. That would help streamline things a little further, allowing you to write the last two lines above as a single line:
MyObject obj = [[MyObject alloc] initWithDictionary:dict];

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.

How do I concatenate strings together in Objective-C?

So I am trying to concatenate a bunch of input strings together as one string so I can save that to a text file.
So far I am trying to write something like this
NSString *tempString = [[NSString alloc]initWithFormat:#"%#%#%#", text1, text2, text3];
The only problem with this is that I need a total of 30 strings stored this way. I need a way to do this without typing out each string name. Is there a way to use a for loop or something to accomplish this? Type the strings like this perhaps?
text(i)
So that the variable name would change each time it went through the for loop. I've tried doing something like this and I can't get it to work. If you can help me with this method or another way that you know to do it I would be very thankful.
Okay, so all of the answers here take the wrong approach (sorry guys).
The fundamental problem is that you are using your "text boxes" as a data source, when they should simply be views. When someone changes the text, you should immediately store them in your model (which could be a simple array) and then reference that model later. (This is part of MVC. Look it up if you aren't familiar, as you should be if you are programming for iOS!)
Here is what I would do. (I'm assuming that your "text boxes" are UITextField's.)
Set the delegate for each text field to your view controller.
Set the tag for each text field to a number which represents the order that you want the strings joined in. (ie 1-30)
If you don't have a separate class for your data model, then setup a declared property in your view controller which stores a reference to a NSMutableArray which can contain all of the strings in order. Let's call it dataSource. In viewDidLoad: set this to an actual mutable array filled with empty values (or previously stored values if you are saving them). The reason that we store empty values is so that we can replace them with the user entered strings for any index, even if they are entered out of order:
- (void)viewDidLoad
{
[super viewDidLoad];
self.dataSource = [[NSMutableArray alloc] initWithCapacity:20];
for(int i = 0; i < 30; i++)
[self.dataSource addObject:#""];
}
Then, use the following text field delegate method which stores the strings into the array as they are entered:
// This is called every time that a text field finishes editing.
- (void)textFieldDidEndEditing:(UITextField *)textField
{
if (textField.tag > 0)
[self.dataSource replaceObjectAtIndex:textField.tag-1 withObject:textField.text];
}
Congratulations! All of your strings are now stored in one array. Now we just have to combine them all:
NSMutableString *theString = [self.dataSource componentsJoinedByString:#""];
Note that I have NOT tested all of this so there may be typos. This should get you pointed in the right direction though!
If you set up your text boxes in Interface Builder with an IBOutletCollection(UITextField) you would have an array of text boxes that you could access the text value using KVC and join them.
//interface
...
#property (nonatomic, strong) IBOutletCollection(UITextField) NSArray *textBoxes;
//implementation
...
NSString *tempString = [[textBoxes valueForKey:#"text"]
componentsJoinedByString:#""];
Using iOS 4's IBOutletCollection
If you programmatically create your text boxes then add them to an array as you create them.
NSMutableString's appendString: method is your friend.
NSArray *strings = [NSArray arrayWithObjects: #"Hi", #" there", #" dude", nil];
NSMutableString *result = [[NSMutableString alloc] init];
for (NSString *string in strings) {
[result appendString:string];
}
NSLog(#"result: %#", result); // result: Hi there dude

How to fill NSArray in compile time?

In Objective-C, how to do something like is
int array[] = {1, 2, 3, 4};
in pure C?
I need to fill NSArray with NSStrings with the smallest overhead (code and/or runtime) as possible.
It's not possible to create an array like you're doing at compile time. That's because it's not a "compile time constant." Instead, you can do something like:
static NSArray *tArray = nil;
-(void)viewDidLoad {
[super viewDidLoad];
tArray = [NSArray arrayWithObjects:#"A", #"B", #"C", nil];
}
If it's truly important that you have this precompiled, then I guess you could create a test project, create the array (or whatever object) you need, fill it, then serialize it using NSKeyedArchiver (which will save it to a file), and then include that file in your app. You will then need to use NSKeyedUnarchiver to unarchive the object for use. I'm not sure what the performance difference is between these two approaches. One advantage to this method is that you don't have a big block of code if you need to initialize an array that includes a lot of objects.
use this
NSArray *array = [NSArray arrayWithObjects:str1,str2, nil];
As far as i understand you need a one-dimentional array
You can use class methods of NSArray.. For instance
NSString *yourString;
NSArray *yourArray = [[NSArray alloc] initWithObjects:yourString, nil];
If you need more, please give some more detail about your issue
Simple as that: NSArray<NSString*> *stringsArray = #[#"Str1", #"Str2", #"Str3", ...]; Modern ObjectiveC allows generics and literal arrays.
If you want shorter code, then NSArray *stringsArray = #[#"Str1", #"Str2", #"Str3", ...];, as the generics are optional and help only when accessing the array elements, thus you can later in the code cast back to the templatized array.

Storing user input from text field into an NSArray

I am trying to store user input text (in this case a book title) into an array so that I can output it in a table view in another xib.
I'm getting stuck trying to store the "bookTitle.text" info into my "userinfoArray". I know it probably has a simple solution and I know how to do it in C++ but not in Objective-C. Any tips, links etc. would be great.
NSMutableArray *userinfoArray = [[NSMutableArray alloc]init];
NSString *tempString = [[NSString alloc]initWithString:[bookTitle text]];
[userinfoArray addObject:tempString];
you can then access it later with:
[userinfoArray objectAtIndex:0];
NSMutableArray is very flexible. with addObject:object you can add as many things as you want, remove them with removeObjectAtIndex:index.
more here: NSMutableArray Class Reference
alternatively if you know what size your array will have you can use a normal NSArray: NSArray Class Reference which will work similar
sebastian
Try
userinfoArray = [NSArray arrayWithObject:[bookTitle text]];
Or if you want to create a longer array with more objetcs then
userinfoArray = [NSArray arrayWithObjects:[bookTitle text], secondObject, thirdObject, nil];
If you want to add or remove objects later then you may want to use NSMutableArray instead.
If this does not answer your question, then please try to be a bit more specific about your problem.

Get data from a PList into UITableView?

I want to maintain a list of records, for each one I maintain the same type of data. I want to use this data in 2 different places:
UITableView that takes from each record the "Name" value
UIViewController that takes all the data to use in different fields.
I assume I should be using a plist to store the data; I also assume that the object that should be receiving the data for the UITableView is NSArray so I can use the cellForRowAtIndexPath method to create the table automatically.
So i created the plist "PLForArr.plist":
It seems that i can only get a NSDictionary when calling the plist using
NSString *path = [[NSBundle mainBundle] pathForResource:#"PLForArr" ofType:#"plist"];
NSArray * myArr = [[NSArray alloc] initWithContentsOfFile:path]; //doesn't work...
NSDictionary * myDict = [[NSDictionary alloc] initWithContentsOfFile:path]; //does work, but who to I make a NSArray out of it / or get the data from here to the UITableView?
I understand that i don't understand something basic here... Can someone take me step by step on:
How to keep the data - should I use plist or something else? Should I have a main record of type array (as I did on the example plist here) or can I just keep it as these Dictionaries without the unnecessary MyArr that I used considering the UITableView end target?
How to call the data - Is there a way to get it into a NSArray or must it get into a NSDictionary?
How to call it into the the UITableView - Can I fill in the lines using a NSDictionary?
Storing the data is an Array or a Dictionary is up to you. But if you want to make changes to it over time you can't store it in the main bundle.
Your pList file is a dictionary that contains an array. See code example below.
You will have to store the dictionary in an array for the data source for your table. See code example below.
Assuming that your UITableView's data source is called tableArray. You can use tableArray to fill in the information in the table and your view. Oh yeah,
NSString *path = [[NSBundle mainBundle] pathForResource:#"PLForArr" ofType:#"plist"];
NSDictionary *myDict = [[NSDictionary alloc] initWithContentsOfFile:path];
NSArray *myArray = [myDict objectForKey:#"MyArray"];
self.tableArray = [myArray copy];
[myArray release];
[myDict release];
This goes in tableView: cellForRowAtIndexPath:
cell.text = [[tableArray objectAtIndex:row]objectForKey:#"Obj Name"];
Storing your data either in a dictionary, or in an array is up to you. Depending on the kind of data you have, you will consider storing unordered collection of objects (dictionary), accessing the entries with keys; or rather in ordered collection (array), using numeric indexes.
It's fine to get arrays from property list files, but the root (top level) object is a dictionary (in the screenshot, "MyArr" isn't the top-level object, it is the key for accessing your array in the top-level dictionary). To get your array from it, simply alloc/init the plist dictionary the way you did, and access the array entry using its key ([myDict objectForKey:#"MyArr"]). Otherwise make sure you set the root object of the property list to be an array, and retry NSArray's initWithContentsOfFile:
The real question seems to be How can I fill the cells with my data ? The table views ask its delegate and dataSource about how many sections, rows in a section, to display. Based on these numbers, it will ask the dataSource for cells. Once again depending on the storage type you've chosen, you will implements these methods a little bit differently, but the concepts remain.
You will probably want to read documentation about :
Property List
Table views