views:

18

answers:

2

Hi all,

I have an app that imports a .CSV file and turns each line into a Core Data object that is stored in a database. The CSV files that I am importing have about 40-something columns and each column maps to an attribute of the Core Data object.

When I started the project, there was only one CSV format that I was working with, so I just wrote 40-something lines of un-elegant static code to import the file, such as:

...
newEntity.yearOfConstruction      = [NSNumber numberWithInt:[[currentRow objectAtIndex:35] integerValue]];
newEntity.assessedValue           = [NSNumber numberWithInt:[[currentRow objectAtIndex:36] integerValue]];
newEntity.squareFootageOfProperty = [NSNumber numberWithInt:[[currentRow objectAtIndex:37] integerValue]];
...

Now the problem that I've run into is that I would like to import other CSV file formats that are ordered differently than those in my original use case.

Rather than write a switch(CSVFormatType) and add additional 40-line sets of code, what is the elegant way to store the CSV column-to-Core Data mapping for an arbitrary number CSV file types and just one set of code to create the Core Data objects?

Thanks!

A: 

Have you considered building a table with the appropriate sets of numbers of fields and positions. Append a new set when a different format appears. Use the data in the generation of your code (input parameter is the Set_Id)?

Ant
A: 

I'm working on something similar right now. Basically I create an format array which has keys for an NSDictionary I want to be created, the index of these keys matches the column in the csv file.
This is what I have until now:

+ (NSArray *)arrayFromCSV:(NSString *)csv format:(NSArray *)format ignoreHeader:(BOOL)ignore {
    NSArray *dataRows = [csv componentsSeparatedByString:@"\n"];
    NSArray *checkrow = [[dataRows objectAtIndex:0] componentsSeparatedByString:@";"];
    NSInteger expectedRowComponents = [checkrow count];
    if ([format count] > expectedRowComponents) {
            // more format keys than components
            return nil;
    }
    NSMutableArray *returnArray = [NSMutableArray array];
    for ( NSInteger i = 0; i < [dataRows count]; i++ ) {
            if (i == 0 && ignore)
                    // ignore first line in csv, "header"
                    continue;
            NSString *row = [dataRows objectAtIndex:i];
            NSArray *rowComponents = [row componentsSeparatedByString:@";"];
            if ( [rowComponents count] != expectedRowComponents )
                continue;
            NSMutableDictionary *tmpDict = [NSMutableDictionary dictionary];
            for ( NSInteger j = 0; j < [format count]; j++ ) {
                if ( [format objectAtIndex:j] != [NSNull null] ) {
                            [tmpDict setObject:[rowComponents objectAtIndex:j] forKey:[format objectAtIndex:j]];
                }
            }
            [returnArray addObject:tmpDict];
        }
    NSLog(@"%@", csv);
    NSLog(@"%@", returnArray);
    return returnArray;
}

But this is only the first step and this supports only a very simple fileformat. Cells with ";" inside are not supported.

if the csv format is like this: firstname ; lastname ; zip ; age
and you want to import firstname, lastname and age you create an importformat like this

format = [NSArray arrayWithObjects: @"firstname", @"lastname", [NSNull null], @"age", nil];

then you'll get an NSDictionary with the keys firstname, lastname, age. And if the format changes you just rearrange the array with the importformat.

fluchtpunkt