NSCaseInsentiveCompare works not as intended? - objective-c

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.

Related

Find a sequence of matching words in two strings

I have a problem when comparing two arrays of NSString.
One of the arrays keeps changing, because it is linked to an UITextField, so when you write anything, it is stored in the array.
-(void) tick: (ccTime) dt{
NSString *level = (NSString *)gameData[2]; //this doesn't change. example: "one two three .."
NSString *text = (NSString *)tf.text; //text field keeps changing as I write
NSArray *separatedText = [text componentsSeparatedByString:#" "];
NSArray *separatedLevel = [level componentsSeparatedByString:#" "];
I want to check if any of the words that you are writing match with any of the words that are stored in the level array.
For example,
Level is: "Comparing two strings"
And I write "comparing"
So this method, would return that 1 word is matching. So if I write "comparing two" it returns that 2 words are matching.
I tried with this code:
for (int i=0;i<separatedText.count;i++){
for (int j=0;j<separatedLevel.count;j++){
if (![separatedText[i] caseInsensitiveCompare:separatedLevel[j]]){
NSLog(#"OK");
}
}else{
NSLog(#"NO");
}
}
}
but it is not working properly.
Any ideas?
Thank you very much.
EDIT
caseInsensitiveCompare does not return a BOOL, but it works for me that way.
If I run this code with level "one two three" and text "one" the result is:
OK
NO
NO
And with "one two" the result is:
NO
OK
NO
When it should be OK OK NO
EDIT 2
Sorry if I expressed myself wrong.
The result that I want is "OK OK NO" when I write 2 words that match
Or maybe, a result that returns the number of matches.
So with the previous example:
Level: "one two three"
Text: "one two"
Result: "2 matching words"
The issue is that you need to increment i if you find a match, and keep a count of the sequential matches you have made.
I have implemented it as a C Function here, but you should have no trouble converting it to an Objective-C class method:
#import <Foundation/Foundation.h>
NSUInteger numSequentialMatches(NSString *s1, NSString *s2) {
NSUInteger sequence = 0, highestSequence = 0;
NSArray *a1 = [s1 componentsSeparatedByString:#" "];
NSArray *a2 = [s2 componentsSeparatedByString:#" "];
for (NSInteger i = 0; i < a1.count; i++) {
for (NSInteger j = 0; j < a2.count; j++) {
if ([a1[i] caseInsensitiveCompare:a2[j]] == NSOrderedSame) {
if (i < a1.count)
i++;
if (++sequence > highestSequence)
highestSequence = sequence;
} else {
sequence = 0;
}
}
}
return highestSequence;
}
int main(int argc, const char **argv) {
#autoreleasepool {
NSLog(#"%lu", numSequentialMatches(#"one two three", #"one"));
NSLog(#"%lu", numSequentialMatches(#"one two three", #"one two"));
}
return 0;
}
$ clang -o array array.m -framework Foundation
$ ./array
2013-10-03 15:21:08.166 array[17194:707] 1
2013-10-03 15:21:08.167 array[17194:707] 2
Try like this using fast enumerator:-
NSArray *separatedText=[NSArray arrayWithObjects:#"one",#"two",#"three",nil];
NSArray *separatedLevel=[NSArray arrayWithObjects:#"one",#"two",nil];
NSString *str1;
NSString *str2;
BOOL isMatch;
for (str1 in separatedText){
isMatch=NO;
for (str2 in separatedLevel){
if (![str1 caseInsensitiveCompare:str2])
{
NSLog(#"OK");
isMatch=YES;
}
}
if (isMatch==NO)
{
NSLog(#"NO");
}
}
Use NSPredicates instead.
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF contains '%#'",separatedText];
NSArray *filteredArray = [separatedLevel filteredArrayUsingPredicate:predicate ];
if ([filteredArray count] == 0){
NSLog(#"No elements");
}
else{
NSLog(#"Have elements");
}

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

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.

Extra space in the end of string

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

Compare two NSStrings

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