This is more a open question than an error-related question, so if you don't like to answer these kinds of questions, please don't flame.
I have a huge (!) list of ships in a .csv file, separated by ,
The matrix is organised like this:
repeated with different data about 500 times.
Now, I want this to be read into objects, which can be used further to populate a UITableView
Currently, I hard-code data into the object files, like this
arrayWithObjectsForTableView = [[NSMutableArray alloc] init];
if ([boatsFromOwner isEqualToString:@"Owner1"]) {
cargoShips* ship = [[cargoShips alloc]init];
ship.name = @"name1";
ship.size = 1000;
ship.owner = @"Owner1";
[self.boatsForOwner addObject:ship];
ship = [[cargoShips alloc]init];
ship.name = @"Name 2";
ship.size = 2000;
ship.owner = @"Owner2";
And so on and on with if-else's. This is a bad method, as 1) Its boring and takes a long time 2) It takes even more time if I want to update the information. So, I figured it would be smarter to read programmatically from the matrix instead of doing it myself. Yeah, captain obvious came for a visit to my brain.
So, to the question!
How can I read the .csv file that looks like this:
add the ships of, say, owner, to a NSMutableArray
, in the shape of objects. (So they can be used to feed my UITableView
with ships.
I would also like to have the option to sort by different stuff, like Country of build, Operator etc. How can I make code that feeds relevant ships read from the .csv into objects? I don't know much programming, so in-depth answers would be very appreciated.
The depth of your processing will determine what sort of data structure is required for this task. This is the method I would use:
1: Read the .csv
file into one giant NSString
object:
NSString *file = [[NSString alloc] initWithContentsOfFile:yourCSVHere];
2: Get the individual lines:
NSArray *allLines = [file componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];
3: For each line, get the individual components:
for (NSString* line in allLines) {
NSArray *elements = [line componentsSeparatedByString:@","];
// Elements now contains all the data from 1 csv line
// Use data from line (see step 4)
}
4: This is where it's up to you. My first thought would be to create a class to store all your data. For example:
@interface Record : NSObject
//...
@property (nonatomic, copy) NSString *name
@property (nonatomic, copy) NSString *owner
// ... etc
@end
4a: Then, back in step 3 create a Record
object for each line and then put all the Record objects into a separate NSArray
(something with larger scope!).
5: Use your NSArray
that contains all your Record
objects as the data source for your UITableView
.
The implementation of Steps 4 and 5 are up to you. That's probably how I would do it though for a medium sized .csv
file.
EDIT: Here's how to generate the Records
.
//
NSMutableArray *someArrayWithLargerScope = [[NSMutableArray alloc] init];
//
NSString *file = [[NSString alloc] initWithContentsOfFile:yourCSVHere];
NSArray *allLines = [file componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet];
for (NSString* line in allLines) {
NSArray *elements = [line componentsSeparatedByString@","];
Record *rec = [[Record alloc] init];
rec.name = [elements objectAtIndex:0];
rec.owner = [elements objectAtIndex:1];
// And so on for each value in the line.
// Note your indexes (0, 1, ...) will be determined by the
// order of the values in the .csv file.
// ...
// You'll need one `Record` property for each value.
// Store the result somewhere
[someArrayWithLargerScope addObject:rec];
}
In terms of the CSV parsing, assuming you can spend the memory it's probably easiest to read in the whole thing to an NSString
, split that on newlines and then split each line on commas, essentially as PLPiper suggests.
At that point I'd take a detour into key-value coding. Give your columns in the CSV file exactly the same name as the properties on your runtime object. Then you can just write something like:
// this method will create an object of the given type then push the values
// from valueRow to the properties named by headingRow. So, ordinarily,
// headingRow will be the first row in your CSV, valueRow will be any other
- (id)populatedObjectOfType:(Class)type withHeadingRow:(NSArray *)headingRow valueRow:(NSArray *)valueRow
{
// we need the count of fields named in the heading row to
// match the count of fields given in this value row
if([headingRow count] != [valueRow count]) return nil;
// autorelease if you're not using ARC
id <NSObject> newInstance = [[type alloc] init];
// we need to enumerate two things simultaneously, so
// we can fast enumerate one but not the other. We'll
// use good old NSEnumerator for the other
NSEnumerator *valueEnumerator = [valueRow objectEnumerator];
for(NSString *propertyName in headingRow)
{
[newInstance setValue:[valueEnumerator nextObject] forKey:propertyName];
}
return newInstance;
}
... elsewhere ....
CargoShip *newShip = [self populateObjectOfType:[CargoShip class] withHeadingRow:[csvFile objectAtIndex:0] valueFor:[csvFile objectAtIndex:1]];
The main caveat is that the built-in mechanisms will convert between scalars and objects but not between objects of different types. So if you had all NSString
and C integer types (short
, int
, NSUInteger
, etc) you'd be fine, but if you had some NSString
s and, say, some NSNumber
s then you would end up with strings stored in the number slots. It looks like you're using C integer types (as is quite normal) so you should be fine.
In terms of filtering, you can use NSPredicate
s. For example, suppose you had an array of CargoShip
s and wanted every one with a size
of at least 500:
NSArray *bigShips = [allShips filteredArrayUsingPredicate:
[NSPredicate predicateWithFormat:@"size > 500"]];
Similarly, for sorting you can throw some NSSortDescriptor
s at the problem. E.g.
NSArray *shipsSortedBySize = [allShips sortedArrayUsingDescriptors:
@[[NSSortDescriptor sortDescriptorWithKey:@"size" ascending:YES]]];
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With