NSMutableArray writing issue? - objective-c

I read in a CSV file and now I am parsing the string and writing the element to a 2D NSMutableArray.
AS I am writing the elements to the 2D array the NSLog outputs the expected element.
But when I am done parsing and writing the whole file to the 2D array, the NSLog shows the last element at each row for each column at that row.
--as if every element in the row was replaced.
??? why, thank you in advanced...
for (int i = 1; i < iM; i++) {//was a 1 to pass over the header
//get the row and break up into columns
nsmarrDummy = [NSMutableArray arrayWithArray:[[nsmarrRow objectAtIndex: i] componentsSeparatedByString: nsstrColParse]];
//write each item to the proper column in the 2d array at the given row
for (int j = 0; j < iN; j++) {//<<--
[[nsmarrData objectAtIndex:j] replaceObjectAtIndex:i-1 withObject: [nsmarrDummy objectAtIndex:j]];
NSLog(#"i:%d j:%d item:%#", i, j, [[nsmarrData objectAtIndex:j] objectAtIndex:i-1]);
}
}
//all the following are the same value, but doing the NSLog as it was writing was correct.
NSLog(#"FINAL: i:%d j:%d item:%#", 0, 4, [[nsmarrData objectAtIndex:4] objectAtIndex:0]);
NSLog(#"FINAL: i:%d j:%d item:%#", 0, 5, [[nsmarrData objectAtIndex:5] objectAtIndex:0]);
NSLog(#"FINAL: i:%d j:%d item:%#", 0, 6, [[nsmarrData objectAtIndex:6] objectAtIndex:0]);

That is some serious thrash I'm seeing in your example, aside from the problem lurking there. Cocoa can do so much of the work for you. It's got thunder appeal. Let it be on your side:
// Get the contents of the file.
// Real apps never ignore their errors or potential nil results.
// For this example, assume path exists.
NSString *fileContents = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:NULL];
NSMutableArray *nsmarrData = [NSMutableArray array];
NSArray *lines = [fileContents componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];
for (NSString *line in lines)
{
// Guard against the last line being only a return.
if ([line length] > 0)
{
NSArray *tokens = [line componentsSeparatedByString:#","]; // assuming no space as well
[nsmarrData addObject:tokens];
}
}
This produces a NSMutableArray filled with NSArrays for each row. Need a NSMutableArray for each row? No problem. Instead of:
[nsmarrData addObject:tokens];
you can use:
[nsmarrData addObject:[NSMutableArray arrayWithArray:tokens]];
Of course, none of this accounts for varying numbers of items on a line; you'll need to beware of that later.
Good luck to you in your endeavors.

Related

Why does replaceObjectAtIndex depend on whether or not I use a new definition in the loop?

I have two codes. Not working is the following:
NSMutableArray *tmpArray = [[NSMutableArray alloc] init];
for (int i=0; i<[dataSetArray count]; i++) {
tmpArray = (NSMutableArray *) [dataSetArray objectAtIndex:i];
// OR use: tmpArray = dataSetArray[i]
... doing stuff
[tmpArray replaceObjectAtIndex:0 withObject:tmpStr];
}
While this works:
for (int i=0; i<[dataSetArray count]; i++) {
NSMutableArray *tmpArray = [[NSMutableArray alloc] initWithArray:[dataSetArray objectAtIndex:i]];
... doing stuff
[tmpArray replaceObjectAtIndex:0 withObject:tmpStr];
}
Two questions:
The first code doesn't yield an NSMutableArray. Why? I declare it
above.
Is there a better way to obtain the same result. I just
dislike defining variables in a loop. This makes the code
unreadable.
--- edit:
Here the full code:
Datatypes are:
dataSetArray: NSMutableArray. However, its contents (i.e. dataSetArray[i]) are NSArrays (I read them into the program from an excel file).
NSString *tmpStr = [[NSString alloc] init];
for (int i=0; i<[dataSetArray count]; i++) {
NSMutableArray *tmpArray = [[NSMutableArray alloc] initWithArray:[dataSetArray objectAtIndex:i]];
for (int j=0; j<[tmpArray count]; j++) {
if ( [dataSetArray[0][j] isEqualToString:#"Number"] ) {continue;}
tmpStr = (NSString *) [tmpArray objectAtIndex:j];
// replace single backslash by double-backslash:
tmpStr = [tmpStr stringByReplacingOccurrencesOfString:#"\\" withString:#"\\\\"];
// replace first dollar sign by "<p>\\[" and second by "\\]</p>"
// Use a methode defined in the NSString+Extension
tmpStr = [tmpStr replaceTexFormulaSigns:tmpStr];
//NSLog(#"j=%d", j);
//NSLog(#"tmpArray is of type: %#", [tmpArray class]);
//NSLog(#" tmpStr is of type: %#", [tmpStr class]);
[tmpArray replaceObjectAtIndex:j withObject:tmpStr];
}
[dataSetArray replaceObjectAtIndex:i withObject:tmpArray];
}
So even if I use your suggestion, I am still facing the same problem with the inner array.
The first code doesn't yield a NSMutableArray. Why? I declare it above.
The declaration of the reference variable tmpArray does not change the type of the referred object. It is still an (immutable) array.
The creation of the mutable array at the very beginning of the first snippet is without any meaning, because the reference to it is overridden.
Is there a better way to obtain the same result. I just dislike defining variables in a loop. This makes the code unreadable.
Yes. The second example works in a way, but do something completely different. (It always creates a new array with a single item in it. No, that's not true. It shouldn't compile at all.)
You should do:
NSMutableArray *tmpArray = [dataSetArray mutableCopy];
for (int i=0; i<[dataSetArray count]; i++)
{
…
[tmpArray replaceObjectAtIndex:i withObject:tmpStr];
}
You should really get some additional knowledge about objects and object references.

Objective-C, making an NSArray of each unique character in an NSString

I've searched around and found several questions answered which were similar to this one, but not exactly the same and I have not been able to get the sample code given in those answers to work for me. I admit this could very likely be my ignorance of Objective-C getting in the way. My situation is this:
I have an NSString from a text file which contains a variety of characters. The length of the string can vary based on what is in the text file. I need to make an array giving each individual character in the string.
I've tried 5 different approaches to the problem (three of them from answers on this site) but each effort I've made to do this has resulted in a) segmentation faults I couldn't track down, b) the array remaining NULL while giving compiler warnings, or c) the array remaining NULL without compiler warnings. In case it matter's, I'm using: gcc -framework Foundation -std=c99 TestCode.m -o TestProgram
Sorry there's no specific code here because I've deleted all my failed efforts in frustration. I guess there's a reason why you shouldn't try to learn a programming language at the same time as trying to learn a new subject that you are applying the language to :)
Would anyone be so helpful as to give me a couple of snippets to work with here?
This is how you would do it.
Read the contents of the file into a NSSString
Enumerate all all the characters
Add them to a NSMutableSet
Get allObjects from the set
In code that roughly translates to this
// 1. Get the contents of the file
NSError *error = NULL;
NSString *textFromFile = [[NSString alloc] initWithContentsOfFile:pathToFile
encoding:NSUTF8StringEncoding
error:&error];
if (!textFromFile) {
// handle error
}
// 2. Enumerate all the characters
// (I'm enumerating composed characters to be able to support for example Chinese)
NSMutableSet *characters = [NSMutableSet set];
[textFromFile enumerateSubstringsInRange:NSMakeRange(0, textFromFile.length)
options:NSStringEnumerationByComposedCharacterSequences
usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
// 3. Add them to a mutable set
[characters addObject:substring];
}];
// 4. Get all the objects from the set (note that it's not sorted)
NSArray *allCharacters = [characters allObjects];
Here's an NSString category that will make an NSArray of all characters in a string:
- (NSArray*)charactersAsArray
{
NSMutableArray* array = [#[] mutableCopy];
for (int i = 0; i < self.length; i++) {
NSRange composedCharRange = [self rangeOfComposedCharacterSequenceAtIndex:i];
NSString* character = [self substringWithRange:composedCharRange];
if (character) {
[array addObject:character];
}
}
return array;
}
Or for unique characters you can use:
- (NSArray*)uniqueCharactersAsArray
{
NSMutableArray* array = [#[] mutableCopy];
for (int i = 0; i < self.length; i++) {
NSRange composedCharRange = [self rangeOfComposedCharacterSequenceAtIndex:i];
NSString* character = [self substringWithRange:composedCharRange];
if (character && ![array containsObject:character]) {
[array addObject:character];
}
}
return array;
}
You can use it like this...
NSString* myString = #"disdiefgdsaéYsué8d9ieo";
NSArray* allCharactersArray = [myString charactersAsArray];
NSArray* uniqueCharactersArray = [myString uniqueCharactersAsArray];

All objects in array (interpreted from csv) being returned as the same object (the last object)

What I am trying to achieve, is to convert a csv file into an array of custom objects, however, my attempts at this seem to result in all of the objects in the array being returned as the same object (the last object in the array).
Before I explain further, here is the code:
- (NSArray *)arrayFromCSVFileName:(NSString *)csvFileName fileType:(NSString *)fileType {
// Convert the file into an NSData object
NSString *studentFilePath = [[NSBundle mainBundle] pathForResource:csvFileName ofType:fileType];
NSData *studentData = [NSData dataWithContentsOfFile:studentFilePath];
// Convert the NSData into an NSString
NSString *csvString = [[NSString alloc] initWithData:studentData encoding:NSUTF8StringEncoding];
// Split each record (line) in the csvDataString into an individual array element (split on the newline character \n)
NSArray *csvArray = [csvString componentsSeparatedByString:#"\n"];
// Create an array to hold the parsed CSV data
NSMutableArray *parsedCSVArray = [[NSMutableArray alloc] init];
NSMutableArray *elementArray = [[NSMutableArray alloc] init];
CGSElement *elementToAdd = [[CGSElement alloc] init];
// Loop through each line of the file
for (int i = 0; i < [csvArray count]; i++) {
// Get a reference to this record (line) as a string, and remove any extranous new lines or alike
NSString *csvRecordString = [[csvArray objectAtIndex:i] stringByReplacingOccurrencesOfString:#"\r" withString:#""];
// Split the line by the comma delimeter
NSArray *csvRecordArray = [csvRecordString componentsSeparatedByString:#","];
// Check that there are actually fields (i.e. this is not a blank line)
if ( ([csvRecordArray count] > 0) && ([[csvRecordArray objectAtIndex:0] length] > 0) ) {
elementToAdd.mass = [[csvRecordArray objectAtIndex:1] floatValue];
elementToAdd.atomicNumber = [[csvRecordArray objectAtIndex:0] intValue];
elementToAdd.name = [csvRecordArray objectAtIndex:2];
elementToAdd.symbol = [csvRecordArray objectAtIndex:3];
elementToAdd.period = [[csvRecordArray objectAtIndex:4] intValue];
[elementArray addObject:elementToAdd];
}
}
for (int i = 0; i < [elementArray count]; i++) {
NSLog(#"%i", i);
CGSElement *current = [elementArray objectAtIndex:i];
NSLog(#"Name = %#", current.name);
}
// Return the parsed array
return elementArray;
}
The custom object in question is the CGSElement object, which I am attempting to fill the elementArray with. However, my debug code (the following section of code):
for (int i = 0; i < [elementArray count]; i++) {
NSLog(#"%i", i);
CGSElement *current = [elementArray objectAtIndex:i];
NSLog(#"Name = %#", current.name);
}
Is resulting, rather than in the return of all of the correct element names, it is returning the last element (to put this in context, ununoctium), 118 times.
After some testing, I can safely say that up until after this point:
elementToAdd.mass = [[csvRecordArray objectAtIndex:1] floatValue];
elementToAdd.atomicNumber = [[csvRecordArray objectAtIndex:0] intValue];
elementToAdd.name = [csvRecordArray objectAtIndex:2];
elementToAdd.symbol = [csvRecordArray objectAtIndex:3];
elementToAdd.period = [[csvRecordArray objectAtIndex:4] intValue];
All of the elements are being correctly defined, rather than just the same element over and over.
Needless to say, I'm stumped as to why it would be returning the same object over and over. Any help would be appreciated.
This line:
CGSElement *elementToAdd = [[CGSElement alloc] init];
Should be inside your loop, just before you try to edit the object and add it to the array. Currently you are repeatedly mutating the same object instead of creating new objects for each record.
You add the same entity all the time. It is crated once before the loop and within the loop it values are changed again and angan and it is added to the array. Naturally all items in the aray carry the same values because it is the same object.
If you want then change the array with an NSSet. To a set an object can only added once and you will end up with a set of 1. That is not the solution of couse, it would just visualize what is happening.
To solve it move this line
CGSElement *elementToAdd = [[CGSElement alloc] init];
to the beginning of the body of the for i loop, so that a new instance is created for every iteration and therefore for every index of the array.

Comparing Strings From Two Files Returns Null in Objective C

Sorry in advance for this being such a beginner question. Here are the steps of what I'm trying to do:
Read two text files (unix word list files for proper names and
regular words)
Separate the text into string
Place the separated strings into an array for each list
Compare the arrays and count the number of matches
For whatever reason, this code continually returns null matches. What might I be doing? Thanks a ton for any help.
int main (int argc, const char * argv[])
{
#autoreleasepool {
// Place discrete words into arrays for respective lists
NSArray *regularwords = [[NSString stringWithContentsOfFile:#"/usr/dict/words" encoding:NSUTF8StringEncoding error:NULL] componentsSeparatedByString:#"\n"];
NSArray *propernames = [[NSString stringWithContentsOfFile:#"/usr/dict/propernames" encoding:NSUTF8StringEncoding error:NULL] componentsSeparatedByString:#"\n"];
// The compare and count loop
NSInteger *counter;
for (int i = 0; i < [propernames count]; i++) {
NSString *stringFromRegularWords = [regularwords objectAtIndex:i];
NSString *properNamesString = [propernames objectAtIndex:i];
if ([properNamesString isEqualToString:stringFromRegularWords]) {
counter++;
}
}
// Print the number of matches
NSLog(#"There was a total of %# matching words", counter);
}
return 0;
}
You're doing objectAtIndex:i, expecting the words to be in exactly same indexes in both files. What you should probably do is add entries from one of the files to an NSMutableSet and then check for membership that way.
// Place discrete words into arrays for respective lists
NSArray *regularwords = [[NSString stringWithContentsOfFile:#"/usr/dict/words" encoding:NSUTF8StringEncoding error:NULL] componentsSeparatedByString:#"\n"];
NSArray *propernames = [[NSString stringWithContentsOfFile:#"/usr/dict/propernames" encoding:NSUTF8StringEncoding error:NULL] componentsSeparatedByString:#"\n"];
// Add each of the words to a set so that we can quickly look them up
NSMutableSet* wordsLookup = [NSMutableSet set];
for (NSString* word in regularwords) {
[wordsLookup addObject:word];
}
NSInteger *counter;
for (NSString *properName in propernames) {
// This efficiently checks if the properName occurs in wordsLookup
if ([wordsLookup containsObject:properName]) {
counter++;
}
}
Note that my example also uses "fast enumeration," i.e. the for ... in syntax. While not necessary to solve your problem, it does make the code shorter and arguably faster.

Problem with NSString and NSRange

I'm having a rather annoying problem here. See, I'm trying to break up a string that I get into individual characters and symbols. The string is always in the form of an equation, like "3x+4" or "x/7+5". I need to separate the string into an array of individual strings. For example, if I had the first equation, I would want to have an NSMutableArray that has "3", "x", "+", and "4". Here is the section of code that I use:
NSMutableArray* list = [[NSMutableArray alloc] initWithCapacity:10];
for (int i = 0; i < [self.equationToGuess length]; i++) {
NSRange range = {i, i};
NSString* string= [[NSString alloc] initWithString:[self.equationToGuess substringWithRange:range]];
[list addObject:string];
}
I've made sure to check if self.equationToGuess always contains an equation using the debugger, and it does. list is also able to get some of the objects, but the problem is that it just puts the last two characters in one shelf on the list. So if I have that "3x+4" equation, this chunk of code puts "3", "x", and "+4" into the code, and then it crashes because it goes beyond the length of the string. Does anyone know how to fix this?
The two values in NSRange are not the starting and ending index. Rather, the first is the starting index and the second is the length of the range. So instead you want your range to be
NSRange range = NSMakeRange(i, 1);
Let's do this with a bit more panache:
NSInteger numberOfCharacters = [self.equationToGuess length];
NSMutableArray *characterArray = [[NSMutableArray alloc] initWithCapacity:numberOfCharacters];
for (NSUInteger idx = 0; idx < numberOfCharacters; idx++) {
NSRange range = NSMakeRange(idx, 1);
[characterArray addObject:[self.equationToGuess substringWithRange:range]];
}
Edit
After a hearty helping of humble pie - this is still not the best way to do it: if your equation has multi-digit coefficients, they will be split up. Have you considered using NSScanner to split the string up instead?
you could also use an alternative solution by getting characters from you string , for that you will have to use the below function of NSString.
- (unichar)characterAtIndex:(NSUInteger)index
.
NSMutableArray* list = [[NSMutableArray alloc] initWithCapacity:10];
NSString* string;
for (int i = 0; i < [self.equationToGuess length]; i++)
{
string = [[NSString alloc] initWithFormat:#"%c",[self.equationToGuess characterAtIndex:i]];
[list addObject:string];
[string release];
string = nil ;
}
NSInteger numberOfCharacters = [self.equationToGuess length];
NSMutableArray *characterArray = [[NSMutableArray alloc] initWithCapacity:numberOfCharacters];
for (NSUInteger idx = 0; idx < numberOfCharacters; idx++) {
[characterArray addObject:[NSString stringWithFormat:#"%c", [self.equationToGuess characterAtIndex:idx]]];
}