My 2nd attempt to find a more efficient solution


#1

In my first attempt awhile back which you can find posted, I first lowercased all the proper names and then used isEqualToString to compare with the lower cased words. Some time has passed and now I am working back through the book and came up with this 2nd solution which I think is more elegant:

//  Interesting names
//
//  Created by Adam G on 5/17/14.
//

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[])
{

    @autoreleasepool {
        
        // Read in a file as a huge string(ignoring the possibility of an error)
        NSString *nameString = [NSString stringWithContentsOfFile:@"/usr/share/dict/propernames"
                                                         encoding:NSUTF8StringEncoding
                                                            error:NULL];

        NSString *wordString = [NSString stringWithContentsOfFile:@"/usr/share/dict/words"
                                                         encoding:NSUTF8StringEncoding
                                                            error:NULL];
        
        // Break it into an array of strings
        NSArray *names = [nameString componentsSeparatedByString:@"\n"];
        NSArray *words = [wordString componentsSeparatedByString:@"\n"];
        int iterations = 0;
        
       for (NSString *n in names) {
                  
            for (NSString *w in words) {
            // iteration count of inner loop    
            iterations++;
            
            // first compare name with the word to see if they are the same regardless of case and
            // then compare them again with isEqualToString to make sure they are not the same case
            // if both conditions are true then print the Name and Word to the log
            if (([n caseInsensitiveCompare:w] == NSOrderedSame) && ([n isEqualToString:w] == NO))
            {
                NSLog(@"Name: %@ Word: %@", n, w);
                // find the first occurrence and then break to the outer loop 
                // iterations are initially 308776083.  When you add the break then iterations fall to 273861512
                // if the same word/name combination appears twice then they are not printed
                   break;
                }
            
            }
            
        }
        NSLog(@"Iterations: %d", iterations);
    }
    return 0;
}


#2

Hi, I’ve got a solution using NSSet, which seems to be faster, than the iteration through every word:

		NSString *nameString = [
			NSString stringWithContentsOfFile	        :	@"/usr/share/dict/propernames"
			encoding							:	NSUTF8StringEncoding
			error								:	NULL
		].lowercaseString;
		
		NSString *wordsString = [
			NSString stringWithContentsOfFile	        :	@"/usr/share/dict/words"
			encoding							:	NSUTF8StringEncoding
			error								:	NULL
		];
		
		NSArray *names = [nameString componentsSeparatedByString:@"\n"];
		NSArray *words = [wordsString componentsSeparatedByString:@"\n"];

		NSMutableSet *set1 = [NSMutableSet setWithArray: names];
		[set1 removeObject:@""];
		NSSet *set2 = [NSSet setWithArray: words];
		[set1 intersectSet: set2];
		NSArray *resultArray = [set1 allObjects];
		
		NSLog(@"\nThere are %lu matches", resultArray.count);

Though I don’t print out every matching word, it can be easily done as in my case the result is a new array containing all the words, that have pairs.


#3

My approach was to lowercase the propernames list like your first attempt.
So what’s the best way to solve this challenge? It seems like there are more than 2, 3 ways to do it…
The most “elegant” way would be an approach that is short and gets the job done most efficiently?

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[])
{

    @autoreleasepool {
        
        // (1) Read in propernames file as a huge string
        // convert *namesList to lowercaseString so that I can find the matched words
        NSString *namesList = [[NSString stringWithContentsOfFile:@"/usr/share/dict/propernames"
                                                         encoding:NSUTF8StringEncoding
                                                            error:NULL] lowercaseString];
        
        // (2) Read in words file as a huge string
        NSString *wordsList = [NSString stringWithContentsOfFile:@"/usr/share/dict/words"
                                                        encoding:NSUTF8StringEncoding
                                                           error:NULL];
        
        // (3) Break them into arrays of strings
        NSArray *names = [namesList componentsSeparatedByString:@"\n"];
        NSArray *words = [wordsList componentsSeparatedByString:@"\n"];
    
        // (4) Use 'fast enumeration' to check the duplicates
        for (NSString *n in names ) {
            for (NSString *i in words) {
                // Look for duplicates
                if ([n isEqualToString:i] == YES) {
                // Print them if found
                    NSLog(@"\n%@ is a duplicate.\n", i);
                }
            }
       
    }
    return 0;
}
}

#4

I added a code at the end that prints how many matches are there. I’m getting 294, but I think the correct answer is 293.
I noticed that I get a “[blank] is a duplicate” at the very end. Why is it doing that? Here’s the last three output:

2014-05-30 16:39:27.972 17_NSArray_Challenge2[46960:303] wolf is a duplicate.
2014-05-30 16:39:27.994 17_NSArray_Challenge2[46960:303] woody is a duplicate.
2014-05-30 16:39:28.013 17_NSArray_Challenge2[46960:303] is a duplicate.

[code]
#import <Foundation/Foundation.h>

int main(int argc, const char * argv[])
{

@autoreleasepool {
    
    // (1) Read in propernames file as a huge string
    // convert *namesList to lowercaseString so that I can find the matched words
    NSString *namesList = [[NSString stringWithContentsOfFile:@"/usr/share/dict/propernames"
                                                     encoding:NSUTF8StringEncoding
                                                        error:NULL] lowercaseString];
    
    // (2) Read in words file as a huge string
    NSString *wordsList = [NSString stringWithContentsOfFile:@"/usr/share/dict/words"
                                                    encoding:NSUTF8StringEncoding
                                                       error:NULL];
    
    // (3) Break them into arrays of strings
    NSArray *names = [namesList componentsSeparatedByString:@"\n"];
    NSArray *words = [wordsList componentsSeparatedByString:@"\n"];

    // (4) Use 'fast enumeration' to check the duplicates
    long matches = 0;
    for (NSString *n in names ) {
        for (NSString *i in words) {
            // Look for duplicates
            if ([n isEqualToString:i]) {
            // Print them if found
                NSLog(@"%@ is a duplicate.\n", i);
                matches++;
            }
            //  How do I get the count of the matches?
        }
}
    NSLog(@"There were %lu matches", matches);
return 0;

}
}[/code]


#5

.


#6

[quote=“burkanov”]Hi, I’ve got a solution using NSSet, which seems to be faster, than the iteration through every word:

		NSString *nameString = [
			NSString stringWithContentsOfFile	        :	@"/usr/share/dict/propernames"
			encoding							:	NSUTF8StringEncoding
			error								:	NULL
		].lowercaseString;
		
		NSString *wordsString = [
			NSString stringWithContentsOfFile	        :	@"/usr/share/dict/words"
			encoding							:	NSUTF8StringEncoding
			error								:	NULL
		];
		
		NSArray *names = [nameString componentsSeparatedByString:@"\n"];
		NSArray *words = [wordsString componentsSeparatedByString:@"\n"];

		NSMutableSet *set1 = [NSMutableSet setWithArray: names];
		[set1 removeObject:@""];
		NSSet *set2 = [NSSet setWithArray: words];
		[set1 intersectSet: set2];
		NSArray *resultArray = [set1 allObjects];
		
		NSLog(@"\nThere are %lu matches", resultArray.count);

Though I don’t print out every matching word, it can be easily done as in my case the result is a new array containing all the words, that have pairs.[/quote]

Excellent!! Wonder why the book doesn’t show some good examples like this of NSSet or MutableSet.


#7

[quote=“hahndrew”]I added a code at the end that prints how many matches are there. I’m getting 294, but I think the correct answer is 293.
I noticed that I get a “[blank] is a duplicate” at the very end. Why is it doing that? Here’s the last three output:

2014-05-30 16:39:27.972 17_NSArray_Challenge2[46960:303] wolf is a duplicate.
2014-05-30 16:39:27.994 17_NSArray_Challenge2[46960:303] woody is a duplicate.
2014-05-30 16:39:28.013 17_NSArray_Challenge2[46960:303] is a duplicate.

[code]
#import <Foundation/Foundation.h>

int main(int argc, const char * argv[])
{

@autoreleasepool {
    
    // (1) Read in propernames file as a huge string
    // convert *namesList to lowercaseString so that I can find the matched words
    NSString *namesList = [[NSString stringWithContentsOfFile:@"/usr/share/dict/propernames"
                                                     encoding:NSUTF8StringEncoding
                                                        error:NULL] lowercaseString];
    
    // (2) Read in words file as a huge string
    NSString *wordsList = [NSString stringWithContentsOfFile:@"/usr/share/dict/words"
                                                    encoding:NSUTF8StringEncoding
                                                       error:NULL];
    
    // (3) Break them into arrays of strings
    NSArray *names = [namesList componentsSeparatedByString:@"\n"];
    NSArray *words = [wordsList componentsSeparatedByString:@"\n"];

    // (4) Use 'fast enumeration' to check the duplicates
    long matches = 0;
    for (NSString *n in names ) {
        for (NSString *i in words) {
            // Look for duplicates
            if ([n isEqualToString:i]) {
            // Print them if found
                NSLog(@"%@ is a duplicate.\n", i);
                matches++;
            }
            //  How do I get the count of the matches?
        }
}
    NSLog(@"There were %lu matches", matches);
return 0;

}
}[/code][/quote]

Must be some blank spaces in the files.


#8

This is my solution that uses a “for” loop inside a “for” loop. What do you think?

#import <Foundation/Foundation.h>

int main (int argc, const char * argv[]) {
    @autoreleasepool {

        NSString *nameString = [NSString stringWithContentsOfFile:@"/usr/share/dict/propernames"
                                                         encoding:NSUTF8StringEncoding
                                                            error:NULL];
        NSArray *names = [nameString componentsSeparatedByString:@"\n"];
        
        NSString *wordsString = [NSString stringWithContentsOfFile:@"/usr/share/dict/words"
                                                         encoding:NSUTF8StringEncoding
                                                             error:NULL];
        NSArray *words = [wordsString componentsSeparatedByString:@"\n"];
        
        for (NSString *p in names) { // This makes the array of "propernames" into organized strings
            for (NSString *w in words) { // This makes the array of "words" into organized strings
                NSRange r = [w rangeOfString:p options:NSCaseInsensitiveSearch]; // This looks for "propernames" inside "words" with a Case Insensitive Search
                if (r.location != NSNotFound) {
                    NSLog(@"%@",w); // Prints the result
                }
            }
        }
    } return 0;
}

#9

From what I found, if I am correct. There are actually 293 words. I just added another if check inside of the second loop to check for the empty word (…[word isNotEqualTo: @""]…). It will not work if you create a space, like so (@" "). I’m fairly new to this so I’m not certain as to why it that empty string has made its way into the array but my solution seems to keep it from being added into the mutable array and its count. Hope this helps.

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
@autoreleasepool
{
NSString *nameString = [NSString stringWithContentsOfFile:@"/usr/share/dict/propernames"
encoding:NSUTF8StringEncoding
error:NULL];

    NSString *wordString = [NSString stringWithContentsOfFile:@"/usr/share/dict/words"
                                                     encoding:NSUTF8StringEncoding
                                                        error:NULL];
    NSString *lowerCaseNames = [nameString lowercaseString];

    NSArray *names = [lowerCaseNames componentsSeparatedByString:@"\n"];
    NSArray *words = [wordString componentsSeparatedByString:@"\n"];
    
    NSMutableArray * muteArray = [NSMutableArray array];
    
    for(NSArray *name in names)
    {
        for(NSArray *word in words)
        {
            if([word isEqualTo:name] && [word isNotEqualTo: @""])
            {
                [muteArray addObject:word];
            }
        }
    }
    
    NSLog(@"%lu matches found.", muteArray.count);
    NSLog(@"Here is the list of matches:");
    for(int i = 0; i < muteArray.count ; i++)
    {
        NSLog(@"%d) %@", i + 1, muteArray[i]);
    }
    
return 0;
}

}


#10

That’s because, when breaking up the string read from the file, you are using “\n” (newline) as the delimiter. There is a newline at the end of each line in the file; since the last line consists of a word and a newline at the end, you also get a null word (word of length 0) as an undesirable bonus.

[Become a competent programmer faster than you can imagine: pretty-function.org]