1. //
  2. //  NameParser.m
  3. //  Port of Greg Miernicki's PHP NameParser
  4. //
  5. //  Created by Mark Pemburn on 2/7/13.
  6. //  Copyright (c) 2013 Pemburnia LLC. All rights reserved.
  7. //
  8.  
  9. #import "NameParser.h"
  10.  
  11. @implementation NameParser {
  12.     NSArray *prefixes;
  13.     NSArray *surnamePrefixes;
  14.     NSArray *suffixes;
  15. }
  16.  
  17. @synthesize prefix, firstName, middleName, lastName, suffix;
  18.  
  19. - (id) initWithFullName: (NSString *) fullName
  20. {
  21.     self = [super init];
  22.     if (self) {
  23.         prefixes = [[NSArray alloc] initWithObjects: @"sr", @"dr", @"doctor", @"miss", @"misses", @"mr", @"mister", @"mrs", @"ms", @"judge", @"sir", @"madam", @"madame", @"AB", @"2ndLt", @"Amn", @"1stLt", @"A1C", @"Capt", @"SrA", @"Maj", @"SSgt", @"LtCol", @"TSgt", @"Col", @"BrigGen", @"1stSgt", @"MajGen", @"SMSgt", @"LtGen", @"1stSgt", @"Gen", @"CMSgt", @"1stSgt", @"CCMSgt", @"CMSAF", @"PVT", @"2LT", @"PV2", @"1LT", @"PFC", @"CPT", @"SPC", @"MAJ", @"CPL", @"LTC", @"SGT", @"COL", @"SSG", @"BG", @"SFC", @"MG", @"MSG", @"LTG", @"1SGT", @"GEN", @"SGM", @"CSM", @"SMA", @"WO1", @"WO2", @"WO3", @"WO4", @"WO5", @"ENS", @"SA", @"LTJG", @"SN", @"LT", @"PO3", @"LCDR", @"PO2", @"CDR", @"PO1", @"CAPT", @"CPO", @"RADM(LH]", @"SCPO", @"RADM(UH]", @"MCPO", @"VADM", @"MCPOC", @"ADM", @"MPCO-CG", @"CWO-2", @"CWO-3", @"CWO-4", @"Pvt", @"2ndLt", @"PFC", @"1stLt", @"LCpl", @"Capt", @"Cpl", @"Maj", @"Sgt", @"LtCol", @"SSgt", @"Col", @"GySgt", @"BGen", @"MSgt", @"MajGen", @"1stSgt", @"LtGen", @"MGySgt", @"Gen", @"SgtMaj", @"SgtMajMC", @"WO-1", @"CWO-2", @"CWO-3", @"CWO-4", @"CWO-5", @"ENS", @"SA", @"LTJG", @"SN", @"LT", @"PO3", @"LCDR", @"PO2", @"CDR", @"PO1", @"CAPT", @"CPO", @"RDML", @"SCPO", @"RADM", @"MCPO", @"VADM", @"MCPON", @"ADM", @"FADM", @"WO1", @"CWO2", @"CWO3", @"CWO4", @"CWO5", nil];
  24.         surnamePrefixes = [[NSArray alloc] initWithObjects: @"bon", @"ben", @"bin", @"da", @"dal", @"de", @"del", @"der", @"de", @"e", @"la", @"le", @"lo", @"los", @"san", @"st", @"ste", @"van", @"vel", @"von", nil];
  25.         suffixes = [[NSArray alloc] initWithObjects: @"dds", @"esq", @"esquire", @"jr", @"sr", @"2", @"i", @"ii", @"iii", @"iv", @"v", @"clu", @"chfc", @"cfp", @"md", @"phd", nil];
  26.        
  27.         [self parse: fullName];
  28.     }
  29.     return self;
  30.    
  31. }
  32.  
  33. - (void) parse: (NSString *) fullName
  34. {
  35.     prefix = @"";
  36.     firstName = @"";
  37.     middleName = @"";
  38.     lastName = @"";
  39.     suffix = @"";
  40.    
  41.     NSArray *nameParts = [[self pregReplace: fullName withString: @" " usingPattern: @"[ \t\n]+"] componentsSeparatedByString: @","];
  42.     int numPieces = [nameParts count];
  43.    
  44.     switch (numPieces) {
  45.         case 1:
  46.             [self withoutComma: nameParts];
  47.             break;
  48.            
  49.         default:
  50.             [self withComma: nameParts];
  51.             break;
  52.     }
  53. }
  54.  
  55. - (void) withComma: (NSArray *)nameParts
  56. {
  57.     int i, s;
  58.     NSString *current;
  59.     NSString *next;
  60.     int numPieces = [nameParts count];
  61.     if ([self inArray: suffixes find: [nameParts objectAtIndex: 1]]) {
  62.         NSArray *subPieces = [[self trim: [nameParts objectAtIndex: 0]] componentsSeparatedByString: @" "];
  63.         int numSubPieces = [subPieces count];
  64.  
  65.         for (i = 0; i < numSubPieces; i++) {
  66.             current = [self trim: [subPieces objectAtIndex: i]];
  67.             if (i < (numSubPieces - 1)) {
  68.                 next = [self trim: [subPieces objectAtIndex: i + 1]];;
  69.             } else {
  70.                 next = @"";
  71.             }
  72.             if (i == 0 &&  [self inArray: prefixes find: current]) {
  73.                 [self setPrefix: current];
  74.                 continue;
  75.             }
  76.             if ([firstName isEqualToString: @""]) {
  77.                 [self setFirstName: current];
  78.                 continue;
  79.             }
  80.             if (i == numSubPieces - 1) {
  81.                 [self setLastNameToCurrent: current];
  82.                 continue;
  83.             }
  84.             if ([self inArray: surnamePrefixes find: current]) {
  85.                 [self setLastNameToCurrent: current];
  86.                 continue;
  87.             }
  88.             if ([next isEqualToString: @"y"] || [next isEqualToString: @"Y"]) {
  89.                 [self setLastNameToCurrent: current];
  90.                 continue;
  91.             }
  92.             if (![lastName isEqualToString: @""]) {
  93.                 lastName = [NSString stringWithFormat: @"%@ %@", lastName, current];
  94.                 continue;
  95.             }
  96.             [self setMiddleNameToCurrent: current];
  97.         }
  98.         suffix = [self trim: [nameParts objectAtIndex: 1]];
  99.         for (i = 2; i < numPieces; i++) {
  100.             suffix = [NSString stringWithFormat: @"%@, %@", suffix, [self trim: [nameParts objectAtIndex: 1]]];
  101.         }
  102.     } else {
  103.         NSArray *subPieces = [[self trim: [nameParts objectAtIndex: 1]] componentsSeparatedByString: @" "];
  104.         int numSubPieces = [subPieces count];
  105.        
  106.         for (i = 0; i < numSubPieces; i++) {
  107.             current = [self trim: [subPieces objectAtIndex: i]];
  108.             if (i < (numSubPieces - 1)) {
  109.                 next = [self trim: [subPieces objectAtIndex: i + 1]];;
  110.             } else {
  111.                 next = @"";
  112.             }
  113.             if (i == 0 &&  [self inArray: prefixes find: current]) {
  114.                 [self setPrefix: current];
  115.                 continue;
  116.             }
  117.             if ([firstName isEqualToString: @""]) {
  118.                 [self setFirstName: current];
  119.                 continue;
  120.             }
  121.             if (i == numSubPieces - 2 && ![next isEqualToString: @""] && [self inArray: suffixes find: next]) {
  122.                 [self setMiddleNameToCurrent: current];
  123.                 suffix = [NSString stringWithFormat: @"%@", next];
  124.                 break;
  125.             }
  126.             if (i == numSubPieces - 1 && [self inArray: suffixes find: current]) {
  127.                 suffix = [NSString stringWithFormat: @"%@", current];
  128.                 continue;
  129.             }
  130.             [self setMiddleNameToCurrent: current];
  131.         }
  132.         NSString *lastPart = [nameParts lastObject];
  133.         if (lastPart != nil && ![lastPart isEqualToString: @""]) {
  134.             if ([lastName isEqualToString: @""]) {
  135.                 suffix = [NSString stringWithFormat: @"%@", lastPart];
  136.                 for (s = 3; s < numPieces; s++) {
  137.                     suffix = [NSString stringWithFormat: @", %@", [self trim: [nameParts objectAtIndex: s]]];
  138.                 }
  139.             } else {
  140.                 for (s = 2; s < numPieces; s++) {
  141.                     suffix = [NSString stringWithFormat: @", %@", [self trim: [nameParts objectAtIndex: s]]];
  142.                 }
  143.             }
  144.         }
  145.         lastName = [NSString stringWithFormat: @"%@", [nameParts objectAtIndex: 0]];
  146.     }
  147. }
  148.  
  149. - (void) withoutComma: (NSArray *)nameParts
  150. {
  151.     NSArray *subPieces = [[self trim: [nameParts objectAtIndex: 0]] componentsSeparatedByString: @" "];
  152.     int numSubPieces = [subPieces count];
  153.     int i;
  154.     NSString *current;
  155.     NSString *next;
  156.    
  157.     for (i = 0; i < numSubPieces; i++) {
  158.         current = [self trim: [subPieces objectAtIndex: i]];
  159.         if (i < (numSubPieces - 1)) {
  160.             next = [self trim: [subPieces objectAtIndex: i + 1]];;
  161.         } else {
  162.             next = @"";
  163.         }
  164.         if (i == 0 &&  [self inArray: prefixes find: current]) {
  165.             [self setPrefix: current];
  166.             continue;
  167.         }
  168.         if ([firstName isEqualToString: @""]) {
  169.             [self setFirstName: current];
  170.             continue;
  171.         }
  172.         if (i == numSubPieces - 2 && ![next isEqualToString: @""] && [self inArray: suffixes find: next]) {
  173.             [self setLastNameToCurrent: current];
  174.             suffix = [NSString stringWithFormat: @"%@", next];
  175.             break;
  176.         }
  177.         if (i == numSubPieces - 1) {
  178.             [self setLastNameToCurrent: current];
  179.             continue;
  180.         }
  181.         if ([self inArray: surnamePrefixes find: current]) {
  182.             [self setLastNameToCurrent: current];
  183.             continue;
  184.         }
  185.         if ([next isEqualToString: @"y"] || [next isEqualToString: @"Y"]) {
  186.             [self setLastNameToCurrent: current];
  187.             continue;
  188.         }
  189.         if (![lastName isEqualToString: @""]) {
  190.             lastName = [NSString stringWithFormat: @"%@ %@", lastName, current];
  191.             continue;
  192.         }
  193.         [self setMiddleNameToCurrent: current];
  194.     }
  195.    
  196. }
  197.  
  198. - (void) setLastNameToCurrent: (NSString *)current
  199. {
  200.     if (![lastName isEqualToString: @""]) {
  201.         lastName = [NSString stringWithFormat: @"%@ %@", lastName, current];
  202.     } else {
  203.         lastName = [NSString stringWithFormat: @"%@", current];
  204.     }
  205. }
  206.  
  207. - (void) setMiddleNameToCurrent: (NSString *)current
  208. {
  209.     if (![middleName isEqualToString: @""]) {
  210.         middleName = [NSString stringWithFormat: @"%@ %@", middleName, current];
  211.     } else {
  212.         middleName = [NSString stringWithFormat: @"%@", current];
  213.     }
  214. }
  215.  
  216. - (BOOL) inArray: (NSArray *)haystack find: (NSString *)needle
  217. {
  218.     needle = [[needle lowercaseString] stringByReplacingOccurrencesOfString: @"." withString: @""];
  219.     needle = [self trim: needle];
  220.     return [haystack containsObject: needle];
  221. }
  222.  
  223. - (NSString *) pregReplace: (NSString *)string withString: (NSString *)replace usingPattern: (NSString *)pattern
  224. {
  225.     NSError *error = NULL;
  226.     NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern: pattern
  227.                                                                            options: NSRegularExpressionCaseInsensitive
  228.                                                                              error: &error];
  229.     NSString *modifiedString = [regex stringByReplacingMatchesInString: string
  230.                                                                options: 0
  231.                                                                  range: NSMakeRange(0, [string length]) withTemplate: replace];
  232.    
  233.     return modifiedString;
  234. }
  235.  
  236. - (NSString *)trim: (NSString *)string
  237. {
  238.     return [string stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceCharacterSet]];
  239. }
  240. @end