data from txt file to NSArray - objective-c

What I would like to do is the following:
I'm using XCode 4.6.1, building an iOS App
I have an textfile (.txt) that contains my data. It is fetched from a server.
The data it contains is like this:
username:userid:realname:clienttype:clientversion:latitude:longitude:number:anothernumber:
So data is seperated by ":" and all data is user defined.
Because there are 2 types of clients: clienttype1 and clienttype2
I've devided the .txt file into an array using
NSArray *dataClient = [datafile componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]]; //to load txt file line for line in NSArray
NSIndexSet *clientindex = [data1 indexesOfObjectsPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {
NSRange range = [(NSString *)obj rangeOfString:#":clienttype1:"];
if (range.location != NSNotFound)
{
return YES;
}
return NO; // Set the index
NSArray *firstClients = [dataClient objectsAtIndexes:clientindex]; // create array with clienttype1 only
Now I have an array with only objects of clienttype1 data.
As this:
username:userid:realname:clienttype1:clientversion:latitude:longitude:number:anothernumber:
username:userid:realname:clienttype1:clientversion:latitude:longitude:number:anothernumber:
How can I separate the data per user (so per line, cause each line is a different user). So that I can use username, userid etc.
As an example plot location by lat. and lon. on a map with username as title. I know how to plot, make annotations etc. It is just how to get to that data.
I was thinking of a way to read the firstClients array line for line to add each type of data into a different array. In the way of: userNameArray, useridArray etc.
In this way I can fetch data per Array. But how to do this.
Any help is welcome!

Ok found my solution. If anything better pops up please advise.
Used this:
for (int i = 0; i < [firstClients count]; i++) {
NSString *oneLine = [atcClients objectAtIndex:i];
NSArray *oneLineSeparated = [oneLine componentsSeparatedByString:#":"];
}
Now I got an Array with: username, userid, realname etc.

Related

Printing the most frequent words in a file(string) Objective-C

New to objective-c, need help to solve this:
Write a function that takes two parameters:
1 a String representing a text document and
2 an integer providing the number of items to return. Implement the function such that it returns a list of Strings ordered by word frequency, the most frequently occurring word first. Use your best judgement to decide how words are separated. Your solution should run in O(n) time where n is the number of characters in the document. Implement this function as you would for a production/commercial system. You may use any standard data structures.
What I tried so far (work in progress): ` // Function work in progress
// -(NSString *) wordFrequency:(int)itemsToReturn inDocument:(NSString *)textDocument ;
// Get the desktop directory (where the text document is)
NSURL *desktopDirectory = [[NSFileManager defaultManager] URLForDirectory:NSDesktopDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:NO error:nil];
// Create full path to the file
NSURL *fullPath = [desktopDirectory URLByAppendingPathComponent:#"document.txt"];
// Load the string
NSString *content = [NSString stringWithContentsOfURL:fullPath encoding:NSUTF8StringEncoding error:nil];
// Optional code for confirmation - Check that the file is here and print its content to the console
// NSLog(#" The string is:%#", content);
// Create an array with the words contain in the string
NSArray *myWords = [content componentsSeparatedByString:#" "];
// Optional code for confirmation - Print content of the array to the console
// NSLog(#"array: %#", myWords);
// Take an NSCountedSet of objects in an array and order those objects by their object count then returns a sorted array, sorted in descending order by the count of the objects.
NSCountedSet *countedSet = [[NSCountedSet alloc] initWithArray:myWords];
NSMutableArray *dictArray = [NSMutableArray array];
[countedSet enumerateObjectsUsingBlock:^(id obj, BOOL *stop) {
[dictArray addObject:#{#"word": obj,
#"count": #([countedSet countForObject:obj])}];
}];
NSLog(#"Words sorted by count: %#", [dictArray sortedArrayUsingDescriptors:#[[NSSortDescriptor sortDescriptorWithKey:#"count" ascending:NO]]]);
}
return 0;
}
This is a classic job for map-reduce. I am very familiar with objective-c, but as far as I know - these concepts are very easily implemented in it.
1st map-reduce is counting the number of occurances.
This step is basically grouping elements according to the word, and then counting them.
map(text):
for each word in text:
emit(word,'1')
reduce(word,list<number>):
emit (word,sum(number))
An alternative for using map-reduce is to use iterative calculation and a hash-map which will be a histogram that counts number of occurances per word.
After you have a a list of numbers and occurances, all you got to do is actually get top k out of them. This is nicely explained in this thread: Store the largest 5000 numbers from a stream of numbers.
In here, the 'comparator' is #occurances of each word, as calculated in previous step.
The basic idea is to use a min-heap, and store k first elements in it.
Now, iterate the remaining of the elements, and if the new one is bigger than the top (minimal element in the heap), remove the top and replace it with the new element.
At the end, you have a heap containing k largest elements, and they are already in a heap - so they are already sorted (though in reversed order, but dealing with it is fairly easy).
Complexity is O(nlogK)
To achieve O(n + klogk) you may use selection algorithm instead of the min-heap solution to get top-k, and then sort the retrieved elements.

Create Instance variables at runtime

I want to create instance variables dynamically at runtime, and I want to add these variables to a category. The number of the instance variables may change based on the configuration/properties file which I am using for defining them.
Any ideas??
Use Associative References - this is tricky, but that is the mechanism invented specifically for your use case.
Here is an example from the link above: first, you define a reference and add it to your object using objc_setAssociatedObject; then you can retrieve the value back by calling objc_getAssociatedObject.
static char overviewKey;
NSArray *array = [[NSArray alloc] initWithObjects:# "One", #"Two", #"Three", nil];
NSString *overview = [[NSString alloc] initWithFormat:#"%#", #"First three numbers"];
objc_setAssociatedObject (
array,
&overviewKey,
overview,
OBJC_ASSOCIATION_RETAIN
);
[overview release];
NSString *associatedObject = (NSString *) objc_getAssociatedObject (array, &overviewKey);
NSLog(#"associatedObject: %#", associatedObject);
objc_setAssociatedObject (
array,
&overviewKey,
nil,
OBJC_ASSOCIATION_ASSIGN
);
[array release];
I'd be inclined to just use a NSMutableDictionary (see NSMutableDictionary Class Reference). Thus, you would have an ivar:
NSMutableDictionary *dictionary;
You'd then initialize it:
dictionary = [NSMutableDictionary dictionary];
You can then save values to it dynamically in code, e.g.:
dictionary[#"name"] = #"Rob";
dictionary[#"age"] = #29;
// etc.
Or, if you are reading from a file and don't know what the names of the keys are going to be, you can do this programmatically, e.g.:
NSString *key = ... // your app will read the name of the field from the text file
id value = ... // your app will read the value of the field from the text file
dictionary[key] = value; // this saves that value for that key in the dictionary
And if you're using an older version of Xcode (before 4.5), the syntax is:
[dictionary setObject:value forKey:key];
Depends on exactly what you want to do, the question is vague but if you want to have several objects or several integers or so on, arrays are the way to go. Say you have a plist with a list of 100 numbers. You can do something sort of like this:
NSArray * array = [NSArray arrayWithContentsOfFile:filePath];
// filePath is the path to the plist file with all of the numbers stored in it as an array
That will give you an array of NSNumbers, you can then turn that into an array of just ints if you want like this;
int intArray [[array count]];
for (int i = 0; i < [array count]; i++) {
intArray[i] = [((NSNumber *)[array objectAtIndex:i]) intValue];
}
Whenever you want to get an integer from a certain position, lets say you want to look at the 5th integer, you would do this:
int myNewInt = intArray[4];
// intArray[0] is the first position so [4] would be the fifth
Just look into using a plist for pulling data, it will them be really easy to create arrays of custom objects or variables in your code by parsing the plist.

How can I handle this in cocoa? Retrieve an entry from a plist file?

I have this small excel file given by a brazilian government authority, it contains records for each city in Brazil, a ZIP Code range for each city and its "city code" .
I need to retrieve for a given city its "city code".
I imagine the best way would be to parse a given zip code for the city and return its "city code", based on the first two columns that display the zip code range.
I am confident that using AppleScript I can compile a plist file for the given Excel file. But can someone point me for a few lines of objectiveC code to retrieve the given entry from a plist file once I parse the ZIP code?
Please see excel file at http://www.idanfe.com/dl/codes.xls.zip
Thanks.
I have uploaded a sample plist file to http://www.idanfe.com/dl/cityCodes.plist
Further explanation:
I will parse a ZIP CODE value like: 01123010 which is in the range of 01001000 and 05895490, so my routines should return me City Code = 3550308 and City Name = São Paulo.
I have no idea how to achieve this, I might have built the sample plist wrong.
I am confident I can build a plist file using AppleScript, reading from the Excel sheet.
But retrieving the City code for a given ZIP CODE range is a puzzle.
+++ EDIT: +++
I think I have solved it, but it looks kind of clumsy, as almost everything I write.
This AppleScript reads the Excel sheet and writes the plist file : http://www.idanfe.com/dl/creating.scpt.zip
Here you find the 1 MB plist file: http://www.idanfe.com/dl/cityCodes.plist.zip
This is the code I wrote to get the City Code I need:
NSString *zipCodeString;
zipCodeString = #"99990000";
NSString* plistPath = [[NSBundle mainBundle] pathForResource:#"cityCodes" ofType:#"plist"];
NSDictionary *cityCodes_dictionary = [NSDictionary dictionaryWithContentsOfFile:plistPath];
NSArray *allKeys = [cityCodes_dictionary allKeys];
int i = 0;
for (i = 0 ; i <= [allKeys count]; i++) {
NSString *someKey = [allKeys objectAtIndex:i];
NSRange range08 = NSMakeRange (0, 8);
NSRange range88 = NSMakeRange (8, 8);
NSString *startZipCode = [someKey substringWithRange:range08];
NSString *finalZipCode = [someKey substringWithRange:range88];
int startZipCodeInt = [startZipCode intValue];
int finalZipCodeInt = [finalZipCode intValue];
if(startZipCodeInt <= [zipCodeString intValue] && finalZipCodeInt >= [zipCodeString intValue]){
NSLog(#"we found a winner");
NSString *cityCode = [NSString stringWithFormat: #"%#",[[cityCodes_dictionary objectForKey:someKey]objectForKey:#"City Code"]];
[cityCodeIBGEField setStringValue:cityCode];
NSLog(#"cityCode = %#",cityCode);
break;
} else {
// NSLog(#"no winners");
}
}
Basically I append the start zipCode and finalZip Code into one string of 16 digits, so I create one single record in the plist file.
Then when searching for the City Code I break the long key (2 zip codes) in 2 (back to normal zipCode) and search to see which record fits the given zipCode I need a cityCode for.
Some how it doesn't look the best for me, but for my own surprise the code is very fast, although in a loop.
I would appreciate comments...
Thanks,
I would use indexOfObjectPassingTest: to do this kind of search. Something like this:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
NSString* plistPath = [[NSBundle mainBundle] pathForResource:#"cityCodes" ofType:#"plist"];
self.cityCodes_dictionary = [NSDictionary dictionaryWithContentsOfFile:plistPath];
[self findCityWithZip:#"01123010"];
}
-(void)findCityWithZip:(NSString *) searchZip {
NSUInteger indx = [self.cityCodes_dictionary.allKeys indexOfObjectPassingTest:^BOOL(NSString *zip, NSUInteger idx, BOOL *stop) {
NSString *startZipCode = [zip substringWithRange:NSMakeRange(0, 8)];
NSString *finalZipCode = [zip substringWithRange:NSMakeRange(8, 8)];
return (searchZip.integerValue < finalZipCode.integerValue && searchZip.integerValue > startZipCode.integerValue);
}];
NSLog(#"%#",[self.cityCodes_dictionary valueForKey:[self.cityCodes_dictionary.allKeys objectAtIndex:indx]]);
}
Reading the plist shouldn't be a problem at all if it is structured as a dictionary:
NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile: #"cities.plist"];
//NSString *zipStr = [dict valueForKey: #"CityNme"]; //this was an example
That's the easy part. The harder part is parsing and structuring the plist file from an xls file.
Or did I misunderstand your question?
PS: to get a quick look at the dictionary's plist structure, create a mock dictionary and NSLog it's description to see how it's supposed to be (or writeToFile to see the file contents or of course refer to the docs)
edit
You load the supplied plist file in a dictionary using the above code. Then you retrieve another dictionary from within it using
NSDictionary *city = [dict valueForKey : #"yourZipCodeHere"];
From that dictionary you get the cityCode like this
NSString *cityCode = [city valueOrKey: #"City Code"];
As for your range problem, I'm not sure I understand it completely. But you can get an array of all the zip codes using
NSArray *zipArray = [dict allKeys];
And then you can simply iterate over that to get the correct zip code.
PS: I don't know much apple script and would be interested in how you converted the xls to plist using it.

How to properly create an array of arrays by reading in data from a file

So far I have:
//read in data from CSV file and separate it into 'row' strings:
// where dataString is simply a CSV file with lines of CSV data
// 31 lines with 9 integers in each line
NSArray *containerArray = [dataString componentsSeparatedByString:#"\n"];
NSArray *rowTemp; //local variable just for my sake
NSMutableArray *tableArray;//mutable array to hold the row arrays
//For each index of containerArray:
//take the string object (string of CSV data) and then,
//create an array of strings to be added into the final tableArray
for (int i = 0; i < [containerArray count]; i++) {
rowTemp = [[containerArray objectAtIndex:i] componentsSeparatedByString:#","];
[tableArray addObject:rowTemp];
}
Then when I try the following, it returns: (null)
NSLog(#"Row 6, cell 1 is %#", [[tableArray objectAtIndex:5] objectAtIndex:0]);
Any ideas? is there a better way?
FYI this data is static and very unlikely to change. Any way to create and populate a static array rather than using a mutable array, would be much appreciated.
Thanks in advance.
I just got the answer! with some help from iPhoneSDK forums, and what I failed to do was actually create the tableArray. What I did in the code above was merely create the variable. NSMutableArray *tableArray; and did not actually create the mutable array I wanted. Instead what I should have done was: NSMutableArray *tableArray = [NSMutableArray arrayWithCapacity: [containerArray count]]; Which worked like a charm.

Is there a way to get Spell Check data from an NSString?

I'm writing a simple shift cipher iPhone app as a pet project, and one piece of functionality I'm currently designing is a "universal" decryption of an NSString, that returns an NSArray, all of NSStrings:
- (NSArray*) decryptString: (NSString*)ciphertext{
NSMutableArray* theDecryptions = [NSMutableArray arrayWithCapacity:ALPHABET];
for (int i = 0; i < ALPHABET; ++i) {
NSString* theNewPlainText = [self decryptString:ciphertext ForShift:i];
[theDecryptions insertObject:theNewPlainText
atIndex:i];
}
return theDecryptions;
}
I'd really like to pass this NSArray into another method that attempts to spell check each individual string within the array, and builds a new array that puts the strings with the fewest typo'd words at lower indicies, so they're displayed first. I'd like to use the system's dictionary like a text field would, so I can match against words that have been trained into the phone by its user.
My current guess is to split a given string up into words, then spell check each with NSSpellChecker's -checkSpellingOfString:StartingAt: and using the number of correct words to sort the Array. Is there an existing library method or well-accepted pattern that would help return such a value for a given string?
Well, I found a solution that works using UIKit/UITextChecker. It correctly finds the user's most preferred language dictionary, but I'm not sure if it includes learned words in the actual rangeOfMisspelledWords... method. If it doesn't, calling [UITextChecker hasLearnedWord] on currentWord inside the bottom if statement should be enough to find user-taught words.
As noted in the comments, it may be prudent to call rangeOfMisspelledWords with each of the top few languages in [UITextChecker availableLanguages], to help multilingual users.
-(void) checkForDefinedWords {
NSArray* words = [message componentsSeparatedByString:#" "];
NSInteger wordsFound = 0;
UITextChecker* checker = [[UITextChecker alloc] init];
//get the first language in the checker's memory- this is the user's
//preferred language.
//TODO: May want to search with every language (or top few) in the array
NSString* preferredLang = [[UITextChecker availableLanguages] objectAtIndex:0];
//for each word in the array, determine whether it is a valid word
for(NSString* currentWord in words){
NSRange range;
range = [checker rangeOfMisspelledWordInString:currentWord
range:NSMakeRange(0, [currentWord length])
startingAt:0
wrap:NO
language:preferredLang];
//if it is valid (no errors found), increment wordsFound
if (range.location == NSNotFound) {
//NSLog(#"%# %#", #"Valid Word found:", currentWord);
wordsFound++;
}
else {
//NSLog(#"%# %#", #"Invalid Word found:", currentWord);
}
}
//After all "words" have been searched, save wordsFound to validWordCount
[self setValidWordCount:wordsFound];
[checker release];
}