Compare two NSStrings - cocoa-touch

In my app there is a mechanism that requires that at a certain point two NSStrings will be the same to do something; for some reason when I compare the two, even when they are the same, it still doesn't recognize that. The code is something like this:
NSString * aString = [self someMethodThatGetsAString];
NSString * bString;
BOOL areStringsTheSame = NO;
while (areStringsTheSame != YES) {
bString = [self someMethodThatTakesNSStringsFromAnArrey];
if (bString == aString) {
areStringsTheSame = YES;
{ }
I even inserted an NSLog() and made sure that at a certain point they were the same (and as far as I know this is what == stands for...), but still it didn't get into the if to change the BOOL value.
Is there another way to do this comparison? Am I missing something?

You can use the method isEqualToString::
if ([bString isEqualToString:aString])
== compares the references (addresses of) the strings, and not the value of the strings.

This approach worked for me:
if ([firstString compare:secondString] == NSOrderedSame) {
//Do something when they are the same
} else {
//Do something when they are different
}

Recently I was shocked by the fact that two NSStrings that resemble each other on NSLog may be different. It is because sometimes NSString can contain a zero width space character. Be aware of that and consider:
#define ZERO_WIDTH_SPACE_STRING #"\u200B"
To conquer this you should clean your string from zero width white space characters before comparing:
NSMutableString *eMailToAdd = [NSMutableString string];
NSMutableCharacterSet *charSet = [[NSCharacterSet whitespaceCharacterSet] mutableCopy];
//[charSet formUnionWithCharacterSet:[NSCharacterSet punctuationCharacterSet]];
NSString *rawStr = [[tokenField textField] text];
for (int i = 0; i < [rawStr length]; i++)
{
if (![charSet characterIsMember:[rawStr characterAtIndex:i]])
{
[eMailToAdd appendFormat:#"%#",[NSString stringWithFormat:#"%c", [rawStr characterAtIndex:i]]];
}
}

Related

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

Algorithm to find anagrams Objective-C

I've got an algorithm to find anagrams within a group of eight-letter words. Effectively it's alphabetizing the letters in the longer word, doing the same with the shorter words one by one, and seeing if they exist in the longer word, like so:
tower = eortw
two = otw
rot = ort
The issue here is that if I look for ort in eortw (or rot in tower), it'll find it, no problem. Rot is found inside tower. However, otw is not inside eortw (or two in tower), because of the R in the middle. Ergo, it doesn't think two is found in tower.
Is there a better way I can do this? I'm trying to do it in Objective-C, and both the eight-letter words and regular words are stored in NSDictionaries (with their normal and alphabetized forms).
I've looked at various other posts re. anagrams on StackOverflow, but none seem to address this particular issue.
Here's what I have so far:
- (BOOL) doesEightLetterWord: (NSString* )haystack containWord: (NSString *)needle {
for (int i = 0; i < [needle length] + 1; i++) {
if (!needle) {
NSLog(#"DONE!");
}
NSString *currentCharacter = [needle substringWithRange:NSMakeRange(i, 1)];
NSCharacterSet *set = [NSCharacterSet characterSetWithCharactersInString: currentCharacter];
NSLog(#"Current character is %#", currentCharacter);
if ([haystack rangeOfCharacterFromSet:set].location == NSNotFound) {
NSLog(#"The letter %# isn't found in the word %#", currentCharacter, haystack);
return FALSE;
} else {
NSLog(#"The letter %# is found in the word %#", currentCharacter, haystack);
int currentLocation = [haystack rangeOfCharacterFromSet: set].location;
currentLocation++;
NSString *newHaystack = [haystack substringFromIndex: currentLocation];
NSString *newNeedle = [needle substringFromIndex: i + 1];
NSLog(#"newHaystack is %#", newHaystack);
NSLog(#"newNeedle is %#", newNeedle);
}
}
}
If you use only part of the letters it isn't a true anagram.
A good algorithm in your case would be to take the sorted strings and compare them letter by letter, skipping mis-matches in the longer word. If you reach the end of the shorter word then you have a match:
char *p1 = shorter_word;
char *p2 = longer_word;
int match = TRUE;
for (;*p1; p1++) {
while (*p2 && (*p2 != *p1)) {
p2++;
}
if (!*p2) {
/* Letters of shorter word are not contained in longer word */
match = FALSE;
}
}
This is one that approach I might take for finding out if one ordered word contained all of the letters of another ordered word. Note that it won't find true anagrams (That simply requires the two ordered strings to be the same) but this does what I think you're asking for:
+(BOOL) does: (NSString* )longWord contain: (NSString *)shortWord {
NSString *haystack = [longWord copy];
NSString *needle = [shortWord copy];
while([haystack length] > 0 && [needle length] > 0) {
NSCharacterSet *set = [NSCharacterSet characterSetWithCharactersInString: [needle substringToIndex:1]];
if ([haystack rangeOfCharacterFromSet:set].location == NSNotFound) {
return NO;
}
haystack = [haystack substringFromIndex: [haystack rangeOfCharacterFromSet: set].location+1];
needle = [needle substringFromIndex: 1];
}
return YES;
}
The simplest (but not most efficient) way might be to use NSCountedSet. We can do this because for counted sets, [a isSubsetOfSet:b] return YES if and only if [a countForObject:object] <= [b countForObject:object] for every object in a.
Let's add a category to NSString to do it:
#interface NSString (lukech_superset)
- (BOOL)lukech_isSupersetOfString:(NSString *)needle;
#end
#implementation NSString (lukech_superset)
- (NSCountedSet *)lukech_countedSetOfCharacters {
NSCountedSet *set = [NSCountedSet set];
[self enumerateSubstringsInRange:NSMakeRange(0, self.length) options:NSStringEnumerationByComposedCharacterSequences usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
[set addObject:substring];
}];
return set;
}
- (BOOL)lukech_isSupersetOfString:(NSString *)needle {
return [[needle lukech_countedSetOfCharacters] isSubsetOfSet:[self lukech_countedSetOfCharacters]];
}
#end

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.

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

String comparison in Objective-C

I've currently got a webserver set up which I communicate over SOAP with my iPhone app. I am returning a NSString containing a GUID and when I attempt to compare this with another NSString I get some strange results.
Why would this not fire? Surely the two strings are a match?
NSString *myString = #"hello world";
if (myString == #"hello world")
return;
Use the -isEqualToString: method to compare the value of two strings. Using the C == operator will simply compare the addresses of the objects.
if ([category isEqualToString:#"Some String"])
{
// Do stuff...
}
You can use case-sensitive or case-insensitive comparison, depending what you need.
Case-sensitive is like this:
if ([category isEqualToString:#"Some String"])
{
// Both strings are equal without respect to their case.
}
Case-insensitive is like this:
if ([category compare:#"Some String" options:NSCaseInsensitiveSearch] == NSOrderedSame)
{
// Both strings are equal with respect to their case.
}
You can compare string with below functions.
NSString *first = #"abc";
NSString *second = #"abc";
NSString *third = [[NSString alloc] initWithString:#"abc"];
NSLog(#"%d", (second == third))
NSLog(#"%d", (first == second));
NSLog(#"%d", [first isEqualToString:second]);
NSLog(#"%d", [first isEqualToString:third]);
Output will be :-
0
1
1
1