Objective c == strings and isEqualToString method not finding match [closed] - objective-c

This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 10 years ago.
I am trying to find out if a 1 letter string appears in the first 2 letters of a different string. I have tried to compare the strings with == as well as the isEqualToString NSString method.
Does anyone have any idea why my code is not able to find a match. It always just returns 0.
I am wanting the methods to be returning 2 as the letter b is in the second letter location in the word (first parameter) and should match the letter (second parameter)
main method:
Finder *f = [[Finder alloc]init];
int position = [f findLetterLocation : #"abc" : #"b"];
NSLog(#"using == %d", position);
int position2 = [f findLetterLocation2 : #"abc" : #"b"];
NSLog(#"using isEqualToString %d", position);
Finder.m
-(int)findLetterLocation: (NSString*)word : (NSString*)letter{
NSRange MyOneRange = {1, 1};
NSRange MyTwoRange = {2, 1};
NSString *firstCharacter = [[NSString alloc] init];
NSString *secondCharacter = [[NSString alloc] init];
firstCharacter = [word substringWithRange:MyOneRange];
secondCharacter = [word substringWithRange:MyTwoRange];
if(firstCharacter == letter){
return 1;
}
if(firstCharacter == letter){
return 2;
}
return 0;
}
findLetterLocation2 is exactly the same as the method above but only replaces the if statements with the ones below:
if([firstCharacter isEqualToString: letter]){
return 1;
}
if([firstCharacter isEqualToString: letter]){
return 2;
}
The output is
2012-12-29 18:14:51.253 test[83101:303] using == 0
2012-12-29 18:14:51.255 test[83101:303] using isEqualToString 0
Thanks!

You're defining a method -[Finder findLetterLocation::]. You want this method to return the 1-indexed location in the first argument ("word") of the second argument ("letter") if it is the first or second letter and otherwise return 0. This implementation will do the trick:
-(int)findLetterLocation: (NSString*)word : (NSString*)letter
{
int res = 0;
if (word &&
letter &&
[letter length] == 1) {
NSRange range = [word rangeOfString:letter];
if (range.location < 2) {
res = range.location + 1;
}
}
return res;
}
This method name is unconventional. A better method name would be
- (int)locationInString:(NSString *)string ofLetter:(NSString *)letter
A still better method name would be
- (int)pfx_oneIndexedLocationInFirstTwoCharactersOfString:(NSString *)string ofCharacter:(NSString *)letter
where pfx is replaced with your project's prefix.
This method's return type should not be int. Instead it should be either NSUInteger or NSInteger, probably NSUInteger since the result will always be 0, 1 or 2.
- (NSUInteger)pfx_oneIndexedLocationInFirstTwoCharactersOfString:(NSString *)string ofCharacter:(NSString *)letter
This method should probably be in a category on NSString rather than in a stand-alone class Finder:
#interface NSString (Locating)
- (NSUInteger)pfx_oneIndexedLocationInFirstTwoCharactersOfCharacter:(NSString *)character
#end
#implementation NSString (Locating)
- (NSUInteger)pfx_oneIndexedLocationInFirstTwoCharactersOfCharacter:(NSString *)character
{
int res = 0;
if (character &&
[character length] == 1) {
NSRange range = [word rangeOfString:letter];
if (range.location < 2) {
res = range.location + 1;
}
}
return res;
}
#end

NSRange uses location as index 0. the characters you are getting are character 2 and character 3.
Also. I believe that NSString will only match with == if you use the same object.
To match strings use -[NSString isEqualToString:]
Hope that helps
You can test with
NSLog(#"First Character = %#", firstCharacter);

Related

How to restrict number of fraction digits when parsing number from string?

I want to restrict the number of fraction digits a user is allowed to enter into a UITextField that only accepts (localized) numeric input.
Example with 4 fraction digits allowed:
Good: 42, 10.123, 12345.2345
Bad: 0.123456, 6.54321
Right now, I'm using NSNumberFormatter's numberFromString: in the UITextField delegate's textField:shouldChangeCharactersInRange:replacementString: to determine whether it's a legal numeric value.
Unfortunately, NSNumberFormatter seems to ignore maximumFractionDigits in numberFromString:. In tests using getObjectValue:forString:range:error: I had the same problem, and range also was the full length of the string afterwards (unless I start entering letters; then range indicates only the part of the string with digits):
NSNumberFormatter* formatter = [[NSNumberFormatter alloc] init];
formatter.maximumFractionDigits = 3;
formatter.roundingMode = NSNumberFormatterRoundHalfUp;
formatter.generatesDecimalNumbers = YES;
NSDecimalNumber* n = (NSDecimalNumber*)[formatter numberFromString:#"10.12345"];
NSLog(#"Number: %#", n.description); // expected: 10.123, but is: 10.12345
How to best restrict the number of fraction digits in user input?
after you get the unrestricted number, you can use stringWithFormat on that number to create a string with a certain number of decimal places.
eg.
double number = myTextField.text.doubleValue;
NSString *restrictedString = [NSString stringWithFormat:#"%.4f", number];
There are a few ways to do this, but the easiest is probably to split the string into two parts (you will have to localize the '.') and check the length of the second part, like this:
- (BOOL)LNNumberIsValid:(NSString *)string
{
NSArray *numArray = [string componentsSeparatedByString:#"."];
if ([numArray count] == 2)
if ([[numArray objectAtIndex:1] length] > 4)
return NO;
return YES;
}
// Tests
NSLog(#"42: %i", [self LNNumberIsValid:#"42"]); // 1
NSLog(#"10.123: %i", [self LNNumberIsValid:#"10.123"]); // 1
NSLog(#"12345.2345: %i", [self LNNumberIsValid:#"12345.2345"]); // 1
NSLog(#"0.123456: %i", [self LNNumberIsValid:#"0.123456"]); // 0
NSLog(#"6.54321: %i", [self LNNumberIsValid:#"6.54321"]); // 0
EDIT:
The problem with the code that you added to your question is that you are printing the description of the NSDecimalNumber, which is not localized or limited to the number of digits. The NSDecimalNumber itself stores everything that you give it, so you need to change the original string (like my example above) if you want to change that. However, once you have your NSDecimalNumber, you can use the same number formatter to convert it back to a string in the format that you like:
NSNumberFormatter* formatter = [[NSNumberFormatter alloc] init];
formatter.maximumFractionDigits = 3;
formatter.roundingMode = NSNumberFormatterRoundHalfUp;
formatter.generatesDecimalNumbers = YES;
NSDecimalNumber* n = (NSDecimalNumber*)[formatter numberFromString:#"10.12345"];
NSString *s = [formatter stringFromNumber:n];
NSLog(#"Number: %#", s); // expected: 10.123, and is: 10.123
The way I solved this is by checking the position of the decimal separator and making sure that the insertion either is before that position or the insertion would not exceed the maximum number of fraction digits.
Also, I check that the input of a new separator does not occur at a place that would lead to more then the allowed fraction digits and that not more than one separators can be inserted
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
NSString *separator = self.numberFormatter.decimalSeparator;
if(string.length == 0) {
// Empty String means deletion, always possible
return YES;
}
// Check for valid characters (0123456789 + decimal Separator)
for (int i = 0; i < [string length]; ++i) {
unichar c = [string characterAtIndex:i];
if (![self.legalCharSet characterIsMember:c])
{
return NO;
}
}
// Checks if input is separator
if([string isEqualToString:separator]) {
// Check that separator insertion would not lead to more than 2 fraction digits and that not more than one separators are inserted
// (the MIN() makes sure that length - kMaxFractionDigits won’t be below 0 as length and location are NSUIntegers)
return range.location >= self.valueField.text.length - MIN(self.valueField.text.length,kMaxFractionDigits) && [self.valueField.text containsString:separator] == NO;
} else {
// Check if a separator is already included in the string
NSRange separatorPos = [self.valueField.text rangeOfString: separator];
if(separatorPos.location != NSNotFound) {
// Make sure that either the input is before the decimal separator or that the fraction digits would not exceed the maximum fraction digits.
NSInteger fractionDigits = self.valueField.text.length - (separatorPos.location + 1);
return fractionDigits + string.length <= kMaxFractionDigits || range.location <= separatorPos.location;
}
}
return YES;
}
The method may not be bullet proof but it should be sufficient for common text insertions.

Replacing several different characters in NSString

I am making an iPad app for personal use and I m struggling with some character replacement in some strings. For example I got an NSString which contains "\t\t\t C D". Now what I want to do is replace every C and every D there is in there with C# and D#. I have managed to do that but unfortunately it doesn't look efficient at all to me.
Here is my code so far:
- (IBAction)buttonPressed:(id)sender
{
if(sender)
{
NSError *error;
NSString *newTab = [[NSString alloc] init];
NSRegularExpression *regexC = [NSRegularExpression regularExpressionWithPattern:#"C" options:0 error:&error];
NSRegularExpression *regexD = [NSRegularExpression regularExpressionWithPattern:#"D" options:0 error:&error];
newTab = [regexC stringByReplacingMatchesInString:self.tab options:0 range:NSMakeRange(0, self.tab.length) withTemplate:#"C#"];
NSString *newTabAfterFirstRegex = [[NSString alloc] initWithString:newTab];
newTabAfterFirstRegex = [regexD stringByReplacingMatchesInString:newTab options:0 range:NSMakeRange(0, newTab.length) withTemplate:#"D#"];
NSLog(#"%#",newTabAfterFirstRegex);
}
}
Plus this is just a small tester code. What I would really like to do is to have an algorithm that checks for instances of all music tabs (C C# D D# E F F# G G# A A# B) in a given string and when the IBAction is triggered I would like each one of them to be replaced by the next one (and B becomes C).
Any ideas would be very much appreciated!
Thank you very much!
You can set a regular expression (e.g. '[A-G]#?') to match certain strings. With method -matchesInString:options:range: you can loop through all the matches (it will give back a range for each match) and use that range to do the replacements.
Regular expressions seem a bit like overkill for this, you could just do two string replacements, so that you don't get all the overhead from regexes, using
- (NSString *)stringByReplacingOccurrencesOfString:(NSString *)target
withString:(NSString *)replacement
and just replace it twice. Also, you don't need to do the NSString allocations, because it creates a reference in the return.
I created the following methods for encryption the other day. I've tested it for your purpose, and it seems to work.
-(NSString *)ReplaceMe:(NSString *)s {
// Putting the source into an array
NSMutableArray *myArray = [[NSMutableArray alloc] init];
int i;
for (i = 0; i < s.length; i++) {
[myArray addObject: [self Mid:s :i :1]];
}
// Creating a string with the revised array
NSMutableString *myString = [NSMutableString new];
for (i = 0; i < s.length; i++) {
[myString appendString:[self Conversion:[myArray objectAtIndex:i]]];
}
// Final
return myString;
}
The method above requires two additional functions.
-(NSString *)Mid:(NSString *)str:(NSInteger)s:(NSInteger)l {
if ((s <= str.length-1) && (s + l <= str.length) && (s >= 0) && (l >= 1)) {
return [str substringWithRange:NSMakeRange(s, l)];
}
else {
return #"";
}
}
The other is...
-(NSString *)Conversion:(NSString *)s {
if ([s isEqualToString:#"C"]) {
return #"C#";
}
else if ([s isEqualToString:#"D"]) {
return #"D#";
}
else {
return s;
}
}
You can put other conversion pairs in the function above. The following is an example as to how to use ReplaceMe.
- (IBAction)clickAction:(id)sender {
textField2.text = [self ReplaceMe:textField1.text];
}
So it's ReplaceMe is quite easy to use.

Check if NSString contains all or some characters

I have an NSString called query which contains ~10 characters.
I would like to check to see if a second NSString called word contains all of the characters in query, or some characters, but no other characters which aren't specified in query.
Also, if there is only one occurrence of the character in the query, there can only be one occurrence of the character in the word.
Please could you tell me how to do this?
NSString *query = #"ABCDEFJAKSUSHFKLAFIE";
NSString *word = #"fearing"; //would pass as NO as there is no 'n' in the query var.
The following answers the first half:
NSCharacterSet *nonQueryChars = [[NSCharacterSet characterSetWithCharactersInString:[query lowercaseString]] invertedSet];
NSRange badCharRange = [[word lowercaseString] rangeOfCharacterFromSet:nonQueryChars];
if (badCharRange.location == NSNotFound) {
// word only has characters in query
} else {
// found unwanted characters in word
}
I need to think about the second half of the requirement.
Ok, the following code should fulfill both requirements:
- (NSCountedSet *)wordLetters:(NSString *)text {
NSCountedSet *res = [NSCountedSet set];
for (NSUInteger i = 0; i < text.length; i++) {
[res addObject:[text substringWithRange:NSMakeRange(i, 1)]];
}
return res;
}
- (void)checkWordAgainstQuery {
NSString *query = #"ABCDEFJAKSUSHFKLAFIE";
NSString *word = #"fearing";
NSCountedSet *queryLetters = [self wordLetters:[query lowercaseString]];
NSCountedSet *wordLetters = [self wordLetters:[word lowercaseString]];
BOOL ok = YES;
for (NSString *wordLetter in wordLetters) {
int wordCount = [wordLetters countForObject:wordLetter];
// queryCount will be 0 if this word letter isn't in query
int queryCount = [queryLetters countForObject:wordLetter];
if (wordCount > queryCount) {
ok = NO;
break;
}
}
if (ok) {
// word matches against query
} else {
// word has extra letter or too many of a matching letter
}
}

UITextChecker without proper nouns

I'm exercising the UITextChecker class to do a quick check on a string for a word-spelling game. Works a little TOO well. Unfortunately, as far as I can tell, the only methods that operate on this class return "correct" words that also include proper nouns. I would like to check my strings against a list of common words that do NOT include proper nouns. Here's my code so far:
//Test the answer for a word
UITextChecker *checker = [[UITextChecker alloc] init];
NSString *testString = wordString;
NSRange range = NSMakeRange(0,0);
range = [checker rangeOfMisspelledWordInString:[testString lowercaseString]
range:NSMakeRange(0, [testString length])
startingAt:0
wrap:NO
language:#"en_US"];
if (range.location == NSNotFound) {
spelledWord = YES;
} else {
spelledWord = NO;
}
Any help would be appreciated!
Not sure if this is the easiest way but you could put a second condition. First store an array with proper nouns (or other words you don't want) elsewhere in your code do a search on Google if you can't think of them. (I've adapted this slightly from a method i use)
if (range.location == NSNotFound) {
int i = 1;
NSString *p;
foundrand = FALSE;
if ([[MyArray sharedKelArray].Myarray count] >2){
////NSLog(#"GOTTEN - %d", choosennumber);
while(i<[[MyArray sharedKelArray].Myarray count])//would check that if equal
{
p = [[[MyArray sharedKelArray].Myarray objectAtIndex:i] NSString];
NSLog(#"Checking word - %d",p);
if (testString == p){
NSLog(#"Matched");
spelledWord = NO;
i = 5 + [[MyArray sharedKelArray].Myarray count];
}
i+=1;
}
spelledWord = YES;
}
}
}

How do I divide NSString into smaller words?

Greetings,
I am new to objective c, and I have the following issue:
I have a NSString:
"There are seven words in this phrase"
I want to divide this into 3 smaller strings (and each smaller string can be no longer than 12 characters in length) but must contain whole words separated by a space, so that I end up with:
String1 = "There are" //(length is 9 including space)
String2 = "seven words"// (length is 11)
String3 = "in this" //(length is 7), with the word "phrase" ignored as this would exceed the maximum length of 12..
Currently I am splitting my original array into an array with:
NSArray *piecesOfOriginalString = [originalString componentsSeparatedByString:#" "];
Then I have multiple "if" statements to sort out situations where there are 3 words, but I want to make this more extensible for any array up to 39 (13 characters * 3 line) letters, with any characters >40 being ignored. Is there an easy way to divide a string based on words or "phrases" up to a certain length (in this case, 12)?
Something similar to this? (Dry-code warning)
NSArray *piecesOfOriginalString = [originalString componentsSeparatedByString:#" "];
NSMutableArray *phrases = [NSMutableArray array];
NSString *chunk = nil;
NSString *lastchunk = nil;
int i, count = [piecesOfOriginalString count];
for (i = 0; i < count; i++) {
lastchunk = [[chunk copy] autorelease];
if (chunk) {
chunk = [chunk stringByAppendingString:[NSString stringWithFormat:#" %#", [piecesOfOriginalString objectAtIndex:i]]];
} else {
chunk = [[[piecesOfOriginalString objectAtIndex:i] copy] autorelease];
}
if ([chunk length] > 12) {
[phrases addObject:lastchunk];
chunk = nil;
}
if ([phrases count] == 3) {
break;
}
}
well, you can keep splitting the string as you're already doing, or you could check out whether NSScanner suits your needs. In any case, you're going to have to do the math yourself.
Thanks McLemore, that is really helpful! I will try this immediately. My current solution is very similar, but less refined, as I hard coded the loops and use individual variable to hold the sub strings (called them TopRow, MidRow, and BottomRow), that and the memory management issue is overlooked... :
int maxLength = 12; // max chars per line (in each string)
int j=0; // for looping, j is the counter for managing the words in the "for" loop
TopRow = nil; //1st string
MidRow = nil; //2nd string
//BottomRow = nil; //third row string (not implemented yet)
BOOL Row01done = NO; // if YES, then stop trying to fill row 1
BOOL Row02done = NO; // if YES, then stop trying to fill row 2
largeArray = #"Larger string with multiple words";
tempArray = [largeArray componentsSeparatedByString:#" "];
for (j=0; j<[tempArray count]; j=j+1) {
if (TopRow == nil) {
TopRow = [tempArray objectAtIndex:j];
}
else {
if (Row01done == YES) {
if (MidRow == nil) {
MidRow = [tempArray objectAtIndex:j];
}
else {
if (Row02done == YES) {
//row 3 stuff goes here... unless I can rewrite as iterative loop...
//will need to uncommend BottomRow = nil; above..
}
else {
if ([MidRow length] + [[tempArray objectAtIndex:j] length] < maxLength) {
MidRow = [MidRow stringByAppendingString:#" "];
MidRow = [MidRow stringByAppendingString:[tempArray objectAtIndex:j]];
}
else {
Row02done = YES;
//j=j-1; // uncomment once BottowRow loop is implemented
}
}
}
}
else {
if (([TopRow length] + [[tempArray objectAtIndex:j] length]) < maxLength) {
TopRow = [TopRow stringByAppendingString:#" "];
TopRow = [TopRow stringByAppendingString:[tempArray objectAtIndex:j]];
}
else {
Row01done = YES;
j=j-1; //end of loop without adding the string to TopRow, subtract 1 from j and start over inside Mid Row
}
}
}
}