Parsing csv file to NSMutableArray - ios

I know the instructions are all over of how to read a .csv file in objective c then pass it to an NSMuatbleArray, but I'm getting problems when I assign it to a mutableArray. I've spent hours of checking online and trying to fix it, but nothing helped.
Here is my objective c code:
NSError *err;
NSString *filePath = [NSString stringWithContentsOfFile:#"/users/Mike/Desktop/Book1.csv" encoding:NSUTF8StringEncoding error:&err];
NSString *replace = [filePath stringByReplacingOccurrencesOfString:#"\"" withString:#""];
NSString *something = [replace stringByReplacingOccurrencesOfString:#"," withString:#"\n"];
NSMutableArray *columns = [[NSMutableArray alloc] initWithObjects:[something componentsSeparatedByString:#"\n"], nil];
NSLog(#"%#", filePath);
NSLog(#"%#", something);
NSLog(#"%#", columns);
Here is the output:
My App[1854:54976] Kitchen,Bathroom,Dinning Room,Living Room
My App[1854:54976] Kitchen
Bathroom
Dinning Room
Living Room
My App[1854:54976] (
(
Kitchen,
Bathroom,
"Dinning Room",
"Living Room"
)
)
The problem is that the output of the array comes with commas and quotations which I eliminated.
What I need is for the array "columns" to come out like the string "something".
Update
I took away the two strings of "replace" and "something" and switched the array to:
collumns = [[NSMutableArray alloc] initWithObjects:[filePath componentsSeparatedByString:#","], nil];
Now I'm having trouble loading it to a table view. Here's my code for that.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"firstCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];
cell.textLabel.text = [columns objectAtIndex:indexPath.row];
return cell;
}
The app just crashes with an unexplaned reason, but when I make another array manually, it works.
This one works:
NSMutableArrayrow = [[NSMutableArray alloc] initWithObjects:#"First", #"Second", nil];

Your code is a bit muddled, and contains one error that is the cause of your unexplained parenthesis.
Why replace quotes with nothing when there are no quotes in the
source data?
Why replace commas with line breaks, and then split the string into
an array of strings on the line breaks (which gets rid of the line
breaks entirely)? Why not just split the string into an array using
commas and skip the intermediate step?
Finally, and most seriously, the method initWithObjects wants a
comma-delimited set of objects, then a nil. You are passing it an
array, and nil. So what you are getting as a result is a mutable
array that contains a single object, an immutable array. This is
almost certainly not what you want.
This line:
NSMutableArray *columns =
[[NSMutableArray alloc] initWithObjects:
[something componentsSeparatedByString:#"\n"], nil];
...is wrong.
You would use initWithObjects like this:
NSMutableArray *columns =
[[NSMutableArray alloc] initWithObjects: #"one", #"two", #"three", nil];
Note how I'm passing in a comma-separated list of objects, and then a nil. Your use of initWithObjects is passing in a single object, an array, and then a nil. You won't get a mutable array that contains the objects from the source array - you'll get a mutable array that contains your starting immutable array.
It should be written like this instead:
NSMutableArray *columns = [[something componentsSeparatedByString:#"\n"]
mutableCopy];
Or better yet, do it in 2 steps so it's clear whats going on:
NSArray *tempArray = [something componentsSeparatedByString:#"\n"];
NSMutableArray *columns = [tempArray mutableCopy];

Related

replacing an NSDecimalNumbers with another in NSArray crashes

I have a NSMutabelArray and I want to do some additions inside of it. I do this by calling a functions with then create a subarray with the items where the calculations have to be done on.
- (NSDecimalNumber *)calculate:(NSMutableArray *)arrayToCalculate {
while ([arrayToCalculate containsObject:(#"+")]) {
NSUInteger signeLocation = [arrayToCalculate indexOfObject:(#"+")];
[arrayToCalculate replaceObjectAtIndex:(signeLocation-1)
withObject:([[arrayToCalculate objectAtIndex:(signeLocation-1)]
decimalNumberByAdding:[arrayToCalculate objectAtIndex:(signeLocation+1)]])];
[arrayToCalculate removeObjectsAtIndexes:
[NSIndexSet indexSetWithIndexesInRange:NSMakeRange((signeLocation), 2)]];
}
return [arrayToCalculate lastObject];
}
I initialised the arrayToCalculate by:
NSMutableArray *subArray =
[inputArray subarrayWithRange:(rangeOfCalculationItems)];
Every time I run this code it crashes. I am pretty sure it is bc I used subarray on an NSMutableArray and initialised it as NSMutableArray even when the message gives me back a NSArray, but I don't know how I could fix it or it is even the problem.
I copied your method and tested it like this:
NSArray *items = #[
[[NSDecimalNumber alloc] initWithString:#"1"],
#"+",
[[NSDecimalNumber alloc] initWithString:#"2"]
];
NSLog(#"%g", [self calculate: [items mutableCopy]].floatValue);
The code works and the printed result was 3. Your issue must be somewhere else. Are you sure your array is in fact mutable? Note [items mutableCopy].

NSArray sortedArrayUsingDescriptors: New Copy or Retain?

According to NSArray class reference there are 4 type of methods to sort array:
1- sortedArrayUsingComparator:
2- sortedArrayUsingSelector:
3- sortedArrayUsingFunction:context:
4- sortedArrayUsingDescriptors:
For first three methods it mentioned :
The new array contains references to the receiving array’s elements, not copies of them.
But for the forth method (descriptor) it mentioned:
A copy of the receiving array sorted as specified by sortDescriptors.
But following example shows like the other 3 methods, descriptor also retain original array and do not return a new copy of it:
NSString *last = #"lastName";
NSString *first = #"firstName";
NSMutableArray *array = [NSMutableArray array];
NSDictionary *dict;
NSMutableString *FN1= [NSMutableString stringWithFormat:#"Joe"];
NSMutableString *LN1= [NSMutableString stringWithFormat:#"Smith"];
NSMutableString *FN2= [NSMutableString stringWithFormat:#"Robert"];
NSMutableString *LN2= [NSMutableString stringWithFormat:#"Jones"];
dict = [NSDictionary dictionaryWithObjectsAndKeys: FN1, first, LN1, last, nil];
[array addObject:dict];
dict = [NSDictionary dictionaryWithObjectsAndKeys: FN2, first, LN2, last, nil];
[array addObject:dict];
// array[0].first = "Joe" , array[0].last = "Smith"
// array[1].first = "Robert" , array[1].last = "Jones"
NSSortDescriptor *lastDescriptor =[[NSSortDescriptor alloc] initWithKey:last
ascending:YES
selector:#selector(localizedCaseInsensitiveCompare:)];
NSSortDescriptor *firstDescriptor =[[NSSortDescriptor alloc] initWithKey:first
ascending:YES
selector:#selector(localizedCaseInsensitiveCompare:)];
NSArray *descriptors = [NSArray arrayWithObjects:lastDescriptor, firstDescriptor, nil];
NSArray *sortedArray = [array sortedArrayUsingDescriptors:descriptors];
// array[1] == sortedArray[0] == ("Robert" , "Jones")
// comparing array entries whether they are same or not:
NSLog(#" %p , %p " , [array objectAtIndex:1] , [sortedArray objectAtIndex:0] );
// 0x10010c520 , 0x10010c520
it shows objects in both arrays are same,
"A copy of the receiving array sorted as specified by sortDescriptors" means that the array object is copied not the elements in the array. The reason the documentation uses the word "copy" is to make it clear that the returned array is not the same array instance as the receiver.
Elements in an array are never copied in Cocoa with the exception of initWithArray:copyItems:YES which will copy the first level items in the original array to the new array. Even then, this copy is done by calling copyWithZone: on the elements, so caveats apply depending on what elements are in your array.
Note that Cocoa is reference counted, so the concept of "deep copies" is not inherently built in for a reason. This is also (in part) the reason why array objects in cocoa come in two flavors (NSArray and NSMutableArray) and are usually immutable (NSArray) instead of as in other languages where there is not usually a concept of immutable and mutable arrays.
see this SO answer for how to get a "deep copy" of an NSArray.

Set value for key of a single NSMutableDictionary from an array of NSMutableArray

While trying to set a single key/value pair in NSMutableDictionary of NSMutableArray like:
[[self.items objectAtIndex:i] setValue:#"value" forKey:#"key"];
self.items is NSMutableArray and it have a list of NSMutableDictionaries
Instead of setting to that single object, it set it to all dictionaries in the list.
I have used this method before. But I don't know what is happening in this case.
I know NSArray have setValue:#"value" forKey:#"key method, but in this case I am using NSMutableArray
Here is a bit more block of code to help clarify my situation:
-(void)setItem:(id)sender
{
for (CellView *cell in self.CollectionView.visibleCells)
{
NSIndexPath *indexPath = [self.CollectionView indexPathForCell:cell];
int i = (indexPath.section * (mainItems.count)/3+ indexPath.row);
if (((UIButton *)sender).tag == i)
{
[[self.items objectAtIndex:i] setValue:#"value" forKey:#"key"];
}
}
}
Call setObject:forKey:, not setValue:forKey:. There is a difference.
Note that NSMutableArray extends NSArray so NSMutableArray has all of the methods of NSArray.
I also recommend you split your line up as well as use modern syntax:
NSMutableDictionary *dict = self.items[i];
dict[#"key"] = #"value";
NSMutableArray is a subclass of NSArray so all the NSArray methods are still there in NSMutatbleArray. You could try pulling it out and putting it back in to figure things out then reassemble your code after...
NSMutableDictionary *d = [self.items objectAtIndex:i];
[d setValue:#"value" forKey:#"key"];
[self.items setObject: d atIndexedSubscript: i];
This is a little more explicit which will allow you to debug it easier (not getting unexpected nils back, etc.).
Ok I got the issue.
I am working on a large pre-existing code. And I come to notice that The MutableDictionary was defined globally and the same object was being added to the MutableArray. So basically all the pointers in the MUtableArray where pointing to a single object.

Sort NSDictionary by value within an inner nested NSDictionary

I'm working on a language learning app. So I have an NSMutableDictionary with 'word' as keys. The objects for these keys are nested NSDictionaries with the keys 'frequency' and 'count'. NSNumbers are the objects for 'frequency' and 'count'.
Here is the initializing code:
NSString* path = [[NSBundle mainBundle] pathForResource:#"french_top_50000"
ofType:#"txt"];
NSString *fh = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:NULL];
self.userWordlist = [[NSMutableDictionary alloc] init];
for (NSString *word in fh) {
NSArray *keyArray = [[NSArray alloc] initWithObjects:#"frequency", #"count", nil];
NSArray *objectArray = [[NSArray alloc] initWithObjects:frequency, count, nil];
NSDictionary *detailsDict = [[NSDictionary alloc] initWithObjects:objectArray forKeys:keyArray];
[self.userWordlist setObject:detailsDict forKey:word];
}
I'm displaying part of this list in a table, and I want to sort by 'frequency', one of the inner keys. I can't figure out how to do this.
In case the first thought is, "Why did you store this in a nested dictionary?", I wanted the words to be keys because in other parts of the app I frequently search to see if a word is in the NSMutableDictionary.
I thought about having a flat dictionary with the following keys:
'word','frequency','count'
... but I'd have to enumerate to check for inclusion of words.
If there are any suggestions for a better data structure strategy I'd love to hear them. I'm going to be checking very frequently for inclusion of 'words' and less frequently will be sorting based on 'frequency' or 'count'.
I've seen lots of question similar to this but they're all for flat dictionaries.
If I understand correctly, use keysSortedByValueUsingComparator: like this:
NSArray *keysByFrequency = [self.userWordlist keysSortedByValueUsingComparator:^NSComparisonResult(NSDictionary* obj1, NSDictionary* obj2) {
return [obj1[#"frequency"] compare:obj2[#"frequency"]];
}];
Then you can iterate keys sorted by their frequency
for (NSString *word in keysByFrequency){
NSDictionary *detailsDict = self.userWordList[word];
// Do whatever...
}

Reading a plist dictionary

I am stuck trying to figure out how this works. I have a plist that is an array of dictionaries. I need to read each dictionary separately within the array and map each dictionary to cells in a tableview. I have a feeling this is extremely simple, but I am not having success accessing the first dictionary and writing each key/value pair to a tableview; then in response to 'next' button, writing 2nd dictionary to tavbeview, then 3rd dictionary, etc. etc. I don't have to go backward, but I do have to go forward displaying contents of each successive dictionary in tableview.
Any tips/code samples will to be much appreciated.
Is this what you mean?
NSDictionary *cellValue = [self.array objectAtIndex:[indexPath row]];
NSString *label = [cellValue objectForKey:#"label"];
[cell.textLabel setText:label];
The array is initialized with the contents of an plist. The plist has a dictionary with a key called label which can then be accessed through objectForKey.
I am not exactly sure what you are doing for writing your dictionaries, but what I did was create a custom class called DataObject added I few methods like addNewItem and writeToFile like this:
NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *finalPath = [documentsDirectory stringByAppendingPathComponent:fileTitle];
[array writeToFile:finalPath atomically:YES];
and for addNewItem:(NSString *)label:
NSDictionary *newItem = [[NSDictionary alloc] initWithObjectsAndKeys:label,#"label", nil];
[array addObject:newItem];
[newItem release];
[self writeTask];
Then simply access them like this:
DataObject *db = [[DataObject alloc] init];
[db addNewItem:youItemTitle];
[db release];
although this would be very bad for memory management.

Resources