Challenge 2 solution - faster by using nested if

Although not the most fast solution to this challenge, this ‘nested if’ does not use information that is yet to be presented in the book. Although using advanced concepts to solve these challenges makes for an interesting learning exercise, I would rather learn step-by-step as guided by the authors’ expertise.

//
//  main.m
//  InterestingNames (nested if)
//
//  Created by James Macak on 3/19/15.
//  Copyright (c) 2015 James Macak. All rights reserved.
//

#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];
        // Break it into an array of strings
        NSArray *names = [nameString componentsSeparatedByString:@"\n"];
        
        // Read in a file as a huge string, ignoring the possibility of an error
        NSString *wordString = [NSString stringWithContentsOfFile:@"/usr/share/dict/words"
                                                         encoding:NSUTF8StringEncoding
                                                            error:NULL];
        
        // Break it into an array of strings
        NSArray *wordsList = [wordString componentsSeparatedByString:@"\n"];
        
        // Find number of items in the arrays
        NSUInteger wordsCount = [wordsList count];
        
        // The next four lines provide some info about the lists but are not essential
        NSUInteger namesCount = [names count];
        
        NSLog(@"The count for the array 'names' is %lu and for 'wordsList' is: %lu", namesCount, wordsCount);
        NSLog(@"The first and last items in 'names' are %@ and %@", names[0],names[namesCount - 2]);
        NSLog(@"The first and last items in 'wordsList' are %@ and %@", wordsList[0],wordsList[wordsCount - 2]);
        
        // Setup a counter for successful matches
        long matchesCount = 0;
        
        // Setup an object to hold the start time of the for loop
        NSDate *startTime = [NSDate date];
        
        // Regarding the following for loop:
        // The first 'if' will be true only when two adjoining words in the array 'wordsList' are case insensitive matches
        // Based on what we know of the words list, these matches are the only words that might also be in the names list
        // The second (nested) 'if' is true if the first or the second of these adjoining words are contained in the 'names' array
        //
        // NOTE: The second part of the 'or' is not needed if we assume the first of the adjoining words is capitalized
        //
        // The improved run time (which is at least 15x) comes from performing the 'containsObject' test
        // on the much shorter 'names' array which has 1309 objects, vs. the 235,887 objects in the 'wordList' array
        
        for (int i = 0; i < wordsCount - 3; i++) {
            if ([[wordsList[i] lowercaseString] isEqual: [wordsList[i+1] lowercaseString]]) {
//              if ([names containsObject:wordsList[i]]) { // see the for loop notes above
                if (([names containsObject:wordsList[i]]) || ([names containsObject:wordsList[i+1]])){
                    NSLog(@"Found match: %@ and %@", wordsList[i], wordsList[i+1]);
                    matchesCount++;
                }
            }
        }
        
        // Save the end time of the for loop
        NSDate *endTime = [NSDate date];
        
        // Print the total number of proper names matches in the regular words list
        NSLog(@"Total matches equals %lu", matchesCount);
        
        // Determine and print the run time of the for loop
        double intervalSeconds = [endTime timeIntervalSinceDate:startTime];
        NSLog(@"The runtime of the for loop was %.2f seconds", intervalSeconds);
    }
    return 0;
}

Output:
2015-03-19 22:36:33.711 InterestingNames (nested if)[89920] The count for the array ‘names’ is 1309 and for ‘wordsList’ is: 235887
2015-03-19 22:36:33.712 InterestingNames (nested if)[89920] The first and last items in ‘names’ are Aaron and Yvonne
2015-03-19 22:36:33.712 InterestingNames (nested if)[89920] The first and last items in ‘wordsList’ are A and Zyzzogeton
2015-03-19 22:36:33.718 InterestingNames (nested if)[89920] Found match: Al and al
2015-03-19 22:36:33.718 InterestingNames (nested if)[89920] Found match: Alan and alan


2015-03-19 22:36:34.246 InterestingNames (nested if)[89920] Found match: Wolf and wolf
2015-03-19 22:36:34.246 InterestingNames (nested if)[89920] Found match: Woody and woody
2015-03-19 22:36:34.250 InterestingNames (nested if)[89920] Total matches equals 293
2015-03-19 22:36:34.250 InterestingNames (nested if)[89920] The runtime of the for loop was 0.54 seconds
Program ended with exit code: 0


As noted in the code comments, this is a pretty fast solution as compared to others that use NSArray and NSString programming.

I’d appreciate hearing your comments, critiques and suggestions for improvement.

Cheers.

The containsObject method is interesting, I did not try it because I could not decipher the code in the documentation. However, your search assumes (correctly) that the two occurrences will happen consecutively. I made an if statement that determines if the string found in words started with uppercase when compared with the names string. This would indicate that the program had found the proper name and not the word. This would be useful in an array where the words/propernames (or other objects) were not arranged in order.

[code]//Read Proper Names file as a string
NSString *properNames = [NSString stringWithContentsOfFile:@"/usr/share/dict/propernames"
encoding:NSUTF8StringEncoding
error:NULL];
//Break *properNames into an array of strings
NSArray *properArray = [properNames componentsSeparatedByString:@"\n"];

    //Read Words file as a string
    NSString *words = [NSString stringWithContentsOfFile:@"/usr/share/dict/words"
                                                encoding:NSUTF8StringEncoding
                                                   error:NULL];
    
    //Break *words into an array of strings
    NSArray *wordsArray = [words componentsSeparatedByString:@"\n"];
    NSDate *startTime = [NSDate date];
    //Go through the proper names array each string at a time with fast enumeration
    for (NSString *p in properArray) {
        //at each enumeration of properArray, search words array for equivilant word
        for (NSString *w in wordsArray){
            //compare *p and *w case insensitive
            int resultI = [p caseInsensitiveCompare:w];
            if (resultI == 0){
                //compare *p and *w sensitive, to see if the match has different cases
                int resultP = [p compare:w];
                if (resultP !=0) {
                    NSLog(@"The words %@ is also the proper name %@.", w, p  );//prints both words if the cases don't match (so one must be a word while one is a proper name
                    
                }
            }
        }
    }

NSDate *endTime =[NSDate date];
double intervalSeconds = [endTime timeIntervalSinceDate:startTime];
    NSLog(@"The loop took %f seconds.", intervalSeconds);
}
return 0;

}
[/code]