Extra space in the end of string - objective-c

I'm going through my array and find at which index the searched word is at. I use that information to put space between the words until i reach the next word. I save that text, and then store it in a variable.
I'm suspecting that my comparison between the lastObject and the arr objectAtIndex: i is not working, but i can't seem to figure out why?
NSArray *arr;
NSScanner *scanner = [NSScanner scannerWithString:_exerciseDocument];
while(![scanner isAtEnd])
{
NSString *buffer;
if([scanner scanCharactersFromSet:[NSCharacterSet whitespaceAndNewlineCharacterSet] intoString:&buffer])
{
[scanner scanUpToString:" " intoString:&buffer];
[scanner scanString:#" " intoString:nil];
}
else
{
}
for(int i=0; i <arr.count; i++)
{
NSString *stringToCheck = (NSString *)[arr objectAtIndex:i];
if([stringToCheck isEqualToString:#"Fokus:"])
{
_descriptionIndex = i;
}
if([stringToCheck isEqualToString:#"Niveau:"])
{
_focusIndex = i;
}
if([stringToCheck isEqualToString:#"Redskab:"])
{
_niveauIndex = i;
}
if([stringToCheck isEqualToString:#"Vanddybde:"])
{
_equipmentIndex = i;
}
}
_descriptiontToTextField = [[NSString alloc]init];
for(int i=1; i <_descriptionIndex; i++)
{
if(![[arr lastObject] isEqual:[arr objectAtIndex:i]])
{
_descriptiontToTextField = [_descriptiontToTextField stringByAppendingString:[ arr objectAtIndex:i]];
_descriptiontToTextField = [_descriptiontToTextField stringByAppendingString:#" "];
}
else
{
_descriptiontToTextField = [_descriptiontToTextField stringByAppendingString:[ arr objectAtIndex:i]];
}
}

First, -[NSString componentsSeparatedByCharactersInSet:] doesn't work the way you may think it does. If there are multiple spaces between words in _exerciseDocument, you're going to end up with a lot of empty strings in arr, which is not what you want. You may want to consider the use of a NSScanner (which, by default, skips whitespace and newline characters) to create your array.
Second, arr contains strings, and no other kind of objects. Given this, you should be using -[NSString isEqualToString:] instead for your comparisons.
Third, is there a reason you're starting your second loop at index 1 instead of index 0?

That if-statement never results to YES because your for-loop always terminates before reaching the last object in your array: i < _descriptionIndex.

i think you want to compare last object in array with all the objects inthe array, but isEqual is used to compare for two objects type not the values in the objects , as you can see
NSArray *arr = [_exerciseDocument componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
this will create arr as array of strings means all the objects are of type [NSString class]
if(![[arr lastObject] isEqual:[arr objectAtIndex:i]])
since all the objects are of same type isEqual will return true so your code will run in else part because of negation in if condition so use isEqualToString for comparing strings

Related

Uppercase random characters in a NSString

I'm trying to figure out the best approach to a problem. I have an essentially random alphanumeric string that I'm generating on the fly:
NSString *string = #"e04325ca24cf20ac6bd6ebf73c376b20ac57192dad83b22602264e92dac076611b51142ae12d2d92022eb2c77f";
You can see that there are no special characters, just numbers and letters, and all the letters are lowercase. Changing all the letters in this string to uppercase is easy:
[string capitalizedString];
The hard part is that I want to capitalize random characters in this string, not all of them. For example, this could be the output on one execution:
E04325cA24CF20ac6bD6eBF73C376b20Ac57192DAD83b22602264e92daC076611b51142AE12D2D92022Eb2C77F
This could be the output on another, since it's random:
e04325ca24cf20aC6bd6eBF73C376B20Ac57192DAd83b22602264E92dAC076611B51142AE12D2d92022EB2c77f
In case it makes this easier, let's say I have two variables as well:
int charsToUppercase = 12;//hardcoded value for how many characters to uppercase here
int totalChars = 90;//total string length
In this instance it would mean that 12 random characters out of the 90 in this string would be uppercased. What I've figured out so far is that I can loop through each char in the string relatively easily:
NSUInteger len = [string length];
unichar buffer[len+1];
[string getCharacters:buffer range:NSMakeRange(0, len)];
NSLog(#"loop through each char");
for(int i = 0; i < len; i++) {
NSLog(#"%C", buffer[i]);
}
Still stuck with selecting random chars in this loop to uppercase, so not all are uppercased. I'm guessing a condition in the for loop could do the trick well, given that it's random enough.
Here's one way, not particularly concerned with efficiency, but not silly efficiency-wise either: create an array characters in the original string, building an index of which ones are letters along the way...
NSString *string = #"e04325ca24cf20ac6bd6ebf73c376b20ac57192dad83b22602264e92dac076611b51142ae12d2d92022eb2c77f";
NSMutableArray *chars = [#[] mutableCopy];
NSMutableArray *letterIndexes = [#[] mutableCopy];
for (int i=0; i<string.length; i++) {
unichar ch = [string characterAtIndex:i];
// add each char as a string to a chars collection
[chars addObject:[NSString stringWithFormat:#"%c", ch]];
// record the index of letters
if ([[NSCharacterSet letterCharacterSet] characterIsMember:ch]) {
[letterIndexes addObject:#(i)];
}
}
Now, select randomly from the letterIndexes (removing them as we go) to determine which letters shall be upper case. Convert the member of the chars array at that index to uppercase...
int charsToUppercase = 12;
for (int i=0; i<charsToUppercase && letterIndexes.count; i++) {
NSInteger randomLetterIndex = arc4random_uniform((u_int32_t)(letterIndexes.count));
NSInteger indexToUpdate = [letterIndexes[randomLetterIndex] intValue];
[letterIndexes removeObjectAtIndex:randomLetterIndex];
[chars replaceObjectAtIndex:indexToUpdate withObject:[chars[indexToUpdate] uppercaseString]];
}
Notice the && check on letterIndexes.count. This guards against the condition where charsToUppercase exceeds the number of chars. The upper bound of conversions to uppercase is all of the letters in the original string.
Now all that's left is to join the chars array into a string...
NSString *result = [chars componentsJoinedByString:#""];
NSLog(#"%#", result);
EDIT Looking discussion in OP comments, you could, instead of acharsToUppercase input parameter, be given a probability of uppercase change as an input. That would compress this idea into a single loop with a little less data transformation...
NSString *string = #"e04325ca24cf20ac6bd6ebf73c376b20ac57192dad83b22602264e92dac076611b51142ae12d2d92022eb2c77f";
float upperCaseProbability = 0.5;
NSMutableString *result = [#"" mutableCopy];
for (int i=0; i<string.length; i++) {
NSString *chString = [string substringWithRange:NSMakeRange(i, 1)];
BOOL toUppercase = arc4random_uniform(1000) / 1000.0 < upperCaseProbability;
if (toUppercase) {
chString = [chString uppercaseString];
}
[result appendString:chString];
}
NSLog(#"%#", result);
However this assumes a given uppercase probability for any character, not any letter, so it won't result in a predetermined number of letters changing case.

Objective C - Split NSString into Array, max count

I am new to Objective-C, so I am trying to split an String into an Array in this format:
NSString *str = #":49:DE:Bahnhofsstr:12:39:11";
NSArray *arr = [str componentsSeparatedByString:#":"];
I receive the following objects in arr:
[#"", #"49", #"DE", #"Bahnhofsstr", #"12", #"39", #"11"]
But I need it in this format:
[#"", #"49", #"DE", #"Bahnhofsstr:12:39:11"]
Anyone have any ideas?
You can use a regular expression. The one you want is this:
^([^:]*):([^:]*):([^:]*):(.*)$
The above matches three sequences of characters without colons in separated by colons and then a fourth group consisting of any kind of character. The ^ at the front and the $ at the end match the beginning and the end of the string respectively otherwise nonsense like 1:2:3:4:49:DE:Bahnhofsstr:12:39:11 would match because there is a match embedded in the string.
The parenthesis delimit capture groups which will be returned to you once the regular expression matching has been done. The first capture group is all the characters up to the first colon. The second capture group is all the characters between the first and second colons. The third capture group is all the characters between the second and third colons and the fourth capture group is all the characters after the third colon.
There is also a zeroth capture group which is the entire matching sequence.
Here's how to use this in Objective-C:
NSString* pattern = #"^([^:]*):([^:]*):([^:]*):(.*)$";
NSString* line = #":49:DE:Bahnhofsstr:12:39:11";
NSRegularExpression* regex = [NSRegularExpression regularExpressionWithPattern: pattern
options: 0
error: &error];
if (regex == nil)
{
NSLog(#"Invalid regular expression %#, %#", pattern, error);
}
else
{
NSArray* matches = [regex matchesInString: line
options: 0
range: NSMakeRange(0, [line length])];
if ([matches count] == 1)
{
// Should only be one match
NSTextCheckingResult* result = [matches objectAtIndex: 0];
NSMutableArray* captureGroups = [[NSMutableArray alloc] init];
// Omit capture group 0 because it will be the whole string
for (int i = 1 ; i < [result numberOfRanges] ; i++)
{
NSRange groupRange = [result rangeAtIndex: i];
NSString* captureGroup = [line substringWithRange: groupRange];
[captureGroups addObject: captureGroup];
}
NSLog(#"The fields are %#", captureGroups);
}
else
{
// match error
}
}
Regular expressions, as proposed by JeremyP, are an obvious solution to this sort of problem.
Some people don't like regexes, though, so another solution is to use NSScanner which is also made to scan strings and read the result into variables. Given that the delimiter is the same for all fields, it even lends itself to use a nice loop, reducing the tedious scanning code.
Here is an example:
NSString *str = #":49:DE:Bahnhofsstr:12:39:11";
const NSUInteger nFields = 4;
NSScanner *myScanner = [NSScanner scannerWithString: str];
NSMutableArray *arr = [NSMutableArray array];
for (NSUInteger i = 0; i < nFields - 1; i++) {
NSString *field;
// The BOOLs here really ought to be checked
BOOL found = [myScanner scanUpToString: #":" intoString: &field];
BOOL passedDelimiter = [myScanner scanString: #":" intoString: NULL];
[arr addObject: field ?: #"" ];
}
NSString *lastField = [[myScanner string] substringFromIndex:[myScanner scanLocation]];
[arr addObject: lastField];
That last line to read the remainder of the string is taken straight from the docs for NSScanner.

NSCaseInsentiveCompare works not as intended?

I am trying to make this code work, the idea is to compare two arrays of strings, first is names and second is words, so I try to find the word from first array, then compare it to all words in second array, and if I get positive result, I print the word out. But it doesn't work as intended, it just prints all the array of names. What's wrong with this code? Maybe there is a more effective way to do this?
for (NSString *n in names) {
for (NSString *m in words) {
if ([n caseInsensitiveCompare:m] == NSOrderedSame) {
NSLog(#"%#", n);
}
}
}
I tried another way and it just keeps printing names one after another. Tried swapping words for names for same result. Something is wrong with comparing to each other.
for (NSString *n in names) {
NSString *newN = [n lowercaseString];
for (NSString *m in words) {
NSString *newM = [m lowercaseString];
if ([newN isEqualToString: newM]) {
NSLog(#"%# is equal to %#", newN, newM );
}
}
}
This thing provides same results! Duh.
NSArray *names = [nameString componentsSeparatedByString:#"\n"];
NSArray *words = [itemsString componentsSeparatedByString:#"\n"];
int i = 0;
int j = 0;
while (i != [names count] ) {
while (j != [words count]) {
if ([[names objectAtIndex:i] caseInsensitiveCompare:[words objectAtIndex:j]] == NSOrderedSame)
{
NSLog(#"Voila! Name is : %#", [names objectAtIndex:i]);
}
j++;
}
j = 0;
i++;
What is wrong? I can't figure out, I tried. If you pick words one by one from either array, you get correct names and words. Words array does not have most names in it as the output I get. I get just names in order, Aaron, Aasomething, etc, they are not in words array.
Actually I have found an answer. The words file contains all the names from the names file. So you just get all the names. All three code variants work as intended.
Well, at least I learned something new today.

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]]];
}