Skip to content

Commit 0d8fa71

Browse files
committed
Fixed minor bug in last name concat. Added tests to assure valid return values.
1 parent 03fa70e commit 0d8fa71

1 file changed

Lines changed: 163 additions & 162 deletions

File tree

‎parse-names.js‎

Lines changed: 163 additions & 162 deletions
Original file line numberDiff line numberDiff line change
@@ -28,168 +28,169 @@ String.prototype.ucfirst = function() {
2828
};
2929

3030
function NameParse() {
31-
var me = this;
32-
33-
// split full names into the following parts:
34-
// - prefix / salutation (Mr., Mrs., etc)
35-
// - given name / first name
36-
// - middle initials
37-
// - surname / last name
38-
// - suffix (II, Phd, Jr, etc)
39-
NameParse.prototype.parse = function (fullastName) {
40-
fullastName = fullastName.trim();
41-
// split into words
42-
var unfilteredNameParts = fullastName.split(" ");
43-
var name = {};
44-
var nameParts = [];
45-
var lastName = "";
46-
var firstName = "";
47-
var initials = "";
48-
var j = 0;
49-
var i = 0;
50-
// completely ignore any words in parentheses
51-
for (i=0; i<unfilteredNameParts.length; i++) {
52-
if (unfilteredNameParts[i].indexOf("(") == -1)
53-
nameParts[j++] = unfilteredNameParts[i];
54-
}
55-
var numWords = nameParts.length;
56-
57-
// is the first word a title? (Mr. Mrs, etc)
58-
var salutation = me.is_salutation(nameParts[0]);
59-
var suffix = me.is_suffix(nameParts[nameParts.length - 1]);
60-
// set the range for the middle part of the name (trim prefixes & suffixes)
61-
var start = (salutation) ? 1 : 0;
62-
var end = (suffix) ? numWords - 1 : numWords;
63-
64-
// concat the first name
65-
for (i=start; i<(end - 1); i++) {
66-
word = nameParts[i];
67-
// move on to parsing the last name if we find an indicator of a compound last name (Von, Van, etc)
68-
// we use i != start to allow for rare cases where an indicator is actually the first name (like "Von Fabella")
69-
if (me.is_compound_lastName(word) && i != start) {
70-
break;
71-
}
72-
// is it a middle initial or part of their first name?
73-
// if we start off with an initial, we'll call it the first name
74-
if (me.is_initial(word)) {
75-
// is the initial the first word?
76-
if (i == start) {
77-
// if so, do a look-ahead to see if they go by their middle name
78-
// for ex: "R. Jason Smith" => "Jason Smith" & "R." is stored as an initial
79-
// but "R. J. Smith" => "R. Smith" and "J." is stored as an initial
80-
if (me.is_initial(nameParts[i + 1])) {
81-
firstName += " " + word.toUpperCase();
82-
} else {
83-
initials += " " + word.toUpperCase();
84-
}
85-
// otherwise, just go ahead and save the initial
86-
} else {
87-
initials += " " + word.toUpperCase();
88-
}
89-
} else {
90-
firstName += " " + me.fix_case(word);
91-
}
92-
}
93-
94-
// check that we have more than 1 word in our string
95-
if ((end - start) > 1) {
96-
// concat the last name
97-
for (i; i<end; i++) {
98-
lastName += " " + me.fix_case(nameParts[i]);
99-
}
100-
} else {
101-
// otherwise, single word strings are assumed to be first names
102-
firstName = me.fix_case(nameParts[i]);
103-
}
104-
105-
// return the various parts in an array
106-
name.salutation = salutation;
107-
name.firstName = firstName.trim();
108-
name.initials = initials.trim();
109-
name.lastName = lastName.trim();
110-
name.suffix = suffix;
111-
112-
return name;
113-
}
114-
// detect and format standard salutations
115-
// I'm only considering english honorifics for now & not words like
116-
this.is_salutation = function (word) {
117-
// ignore periods
118-
word = word.replace(".","").toLowerCase();
119-
// returns normalized values
120-
if (word == "mr" || word == "master" || word == "mister") {
121-
return "Mr.";
122-
} else if (word == "mrs") {
123-
return "Mrs.";
124-
} else if (word == "miss" || word == "ms") {
125-
return "Ms.";
126-
} else if (word == "dr") {
127-
return "Dr.";
128-
} else if (word == "rev") {
129-
return "Rev.";
130-
} else if (word == "fr") {
131-
return "Fr.";
132-
} else {
133-
return false;
134-
}
135-
}
136-
137-
// detect and format common suffixes
138-
this.is_suffix = function (word) {
139-
// ignore periods
140-
word = word.replace(".","").toLowerCase();
141-
// these are some common suffixes - what am I missing?
142-
var suffixArray = ['I','II','III','IV','V','Senior','Junior','Jr','Sr','PhD','APR','RPh','PE','MD','MA','DMD','CME'];
143-
for (var i=0; i<suffixArray.length; i++) {
144-
if (suffixArray[i].toLowerCase() == word) {
145-
return suffixArray[i];
146-
}
147-
}
148-
return false;
149-
}
150-
151-
// detect compound last names like "Von Fange"
152-
this.is_compound_lastName = function (word) {
153-
word = word.toLowerCase();
154-
// these are some common prefixes that identify a compound last names - what am I missing?
155-
words = ['vere','von','van','de','del','della','di','da','pietro','vanden','du','st.','st','la','lo','ter'];
156-
return words.in_array(word);
157-
}
158-
159-
// single letter, possibly followed by a period
160-
this.is_initial = function (word) {
161-
// ignore periods
162-
word = word.replace(".","");
163-
return (word.length == 1);
164-
}
31+
var me = this;
32+
33+
// split full names into the following parts:
34+
// - prefix / salutation (Mr., Mrs., etc)
35+
// - given name / first name
36+
// - middle initials
37+
// - surname / last name
38+
// - suffix (II, Phd, Jr, etc)
39+
NameParse.prototype.parse = function (fullastName) {
40+
fullastName = fullastName.trim();
41+
// split into words
42+
var unfilteredNameParts = fullastName.split(" ");
43+
var name = {};
44+
var nameParts = [];
45+
var lastName = "";
46+
var firstName = "";
47+
var initials = "";
48+
var j = 0;
49+
var i = 0;
50+
// completely ignore any words in parentheses
51+
for (i=0; i<unfilteredNameParts.length; i++) {
52+
if (unfilteredNameParts[i].indexOf("(") == -1) {
53+
nameParts[j++] = unfilteredNameParts[i];
54+
}
55+
}
56+
var numWords = nameParts.length;
57+
// is the first word a title? (Mr. Mrs, etc)
58+
var salutation = me.is_salutation(nameParts[0]);
59+
var suffix = me.is_suffix(nameParts[nameParts.length - 1]);
60+
// set the range for the middle part of the name (trim prefixes & suffixes)
61+
var start = (salutation) ? 1 : 0;
62+
var end = (suffix) ? numWords - 1 : numWords;
63+
64+
// concat the first name
65+
for (i=start; i<(end - 1); i++) {
66+
word = nameParts[i];
67+
// move on to parsing the last name if we find an indicator of a compound last name (Von, Van, etc)
68+
// we use i != start to allow for rare cases where an indicator is actually the first name (like "Von Fabella")
69+
if (me.is_compound_lastName(word) && i != start) {
70+
break;
71+
}
72+
// is it a middle initial or part of their first name?
73+
// if we start off with an initial, we'll call it the first name
74+
if (me.is_initial(word)) {
75+
// is the initial the first word?
76+
if (i == start) {
77+
// if so, do a look-ahead to see if they go by their middle name
78+
// for ex: "R. Jason Smith" => "Jason Smith" & "R." is stored as an initial
79+
// but "R. J. Smith" => "R. Smith" and "J." is stored as an initial
80+
if (me.is_initial(nameParts[i + 1])) {
81+
firstName += " " + word.toUpperCase();
82+
} else {
83+
initials += " " + word.toUpperCase();
84+
}
85+
// otherwise, just go ahead and save the initial
86+
} else {
87+
initials += " " + word.toUpperCase();
88+
}
89+
} else {
90+
firstName += " " + me.fix_case(word);
91+
}
92+
}
93+
94+
// check that we have more than 1 word in our string
95+
if ((end - start) > 1) {
96+
// concat the last name
97+
for (j=i; j<end; j++) {
98+
lastName += " " + me.fix_case(nameParts[j]);
99+
}
100+
} else {
101+
// otherwise, single word strings are assumed to be first names
102+
firstName = me.fix_case(nameParts[i]);
103+
}
104+
105+
// return the various parts in an array
106+
name.salutation = (salutation != false) ? salutation : "";
107+
name.firstName = (firstName != "") ? firstName.trim() : "";
108+
name.initials = (initials != "") ? initials.trim() : "";
109+
name.lastName = (lastName != "") ? lastName.trim() : "";
110+
name.suffix = (suffix != false) ? suffix : "";
111+
112+
return name;
113+
};
114+
115+
// detect and format standard salutations
116+
// I'm only considering english honorifics for now & not words like
117+
this.is_salutation = function (word) {
118+
// ignore periods
119+
word = word.replace(".","").toLowerCase();
120+
// returns normalized values
121+
if (word == "mr" || word == "master" || word == "mister") {
122+
return "Mr.";
123+
} else if (word == "mrs") {
124+
return "Mrs.";
125+
} else if (word == "miss" || word == "ms") {
126+
return "Ms.";
127+
} else if (word == "dr") {
128+
return "Dr.";
129+
} else if (word == "rev") {
130+
return "Rev.";
131+
} else if (word == "fr") {
132+
return "Fr.";
133+
} else {
134+
return false;
135+
}
136+
};
137+
138+
// detect and format common suffixes
139+
this.is_suffix = function (word) {
140+
// ignore periods
141+
word = word.replace(".","").toLowerCase();
142+
// these are some common suffixes - what am I missing?
143+
var suffixArray = ['I','II','III','IV','V','Senior','Junior','Jr','Sr','PhD','APR','RPh','PE','MD','MA','DMD','CME'];
144+
for (var i=0; i<suffixArray.length; i++) {
145+
if (suffixArray[i].toLowerCase() == word) {
146+
return suffixArray[i];
147+
}
148+
}
149+
return false;
150+
};
151+
152+
// detect compound last names like "Von Fange"
153+
this.is_compound_lastName = function (word) {
154+
word = word.toLowerCase();
155+
// these are some common prefixes that identify a compound last names - what am I missing?
156+
var words = ['vere','von','van','de','del','della','di','da','pietro','vanden','du','st.','st','la','lo','ter'];
157+
return words.in_array(word);
158+
};
159+
160+
// single letter, possibly followed by a period
161+
this.is_initial = function (word) {
162+
// ignore periods
163+
word = word.replace(".","");
164+
return (word.length == 1);
165+
};
165166

166-
// detect mixed case words like "McDonald"
167-
// returns false if the string is all one case
168-
this.is_camel_case = function (word) {
169-
var ucReg = /|[A-Z]+|s/;
170-
var lcReg = /|[a-z]+|s/;
171-
return (word.match(ucReg) != null && word.match(lcReg) != null);
172-
}
173-
174-
// ucfirst words split by dashes or periods
175-
// ucfirst all upper/lower strings, but leave camelcase words alone
176-
this.fix_case = function (word) {
177-
// uppercase words split by dashes, like "Kimura-Fay"
178-
word = me.safe_ucfirst("-",word);
179-
// uppercase words split by periods, like "J.P."
180-
word = me.safe_ucfirst(".",word);
181-
return word;
182-
}
167+
// detect mixed case words like "McDonald"
168+
// returns false if the string is all one case
169+
this.is_camel_case = function (word) {
170+
var ucReg = /|[A-Z]+|s/;
171+
var lcReg = /|[a-z]+|s/;
172+
return (word.match(ucReg) != null && word.match(lcReg) != null);
173+
};
174+
175+
// ucfirst words split by dashes or periods
176+
// ucfirst all upper/lower strings, but leave camelcase words alone
177+
this.fix_case = function (word) {
178+
// uppercase words split by dashes, like "Kimura-Fay"
179+
word = me.safe_ucfirst("-",word);
180+
// uppercase words split by periods, like "J.P."
181+
word = me.safe_ucfirst(".",word);
182+
return word;
183+
};
183184

184-
// helper this.for fix_case
185-
this.safe_ucfirst = function (seperator, word) {
186-
var words = [];
187-
// uppercase words split by the seperator (ex. dashes or periods)
188-
parts = word.split(seperator);
189-
for (var i=0; i<parts.length; i++) {
190-
var thisWord = parts[i];
191-
words[i] = (me.is_camel_case(thisWord)) ? thisWord : thisWord.ucfirst.toLowerCase();
192-
}
193-
return words.implode(seperator);
194-
}
185+
// helper this.for fix_case
186+
this.safe_ucfirst = function (seperator, word) {
187+
var words = [];
188+
// uppercase words split by the seperator (ex. dashes or periods)
189+
parts = word.split(seperator);
190+
for (var i=0; i<parts.length; i++) {
191+
var thisWord = parts[i];
192+
words[i] = (me.is_camel_case(thisWord)) ? thisWord : thisWord.ucfirst.toLowerCase();
193+
}
194+
return words.implode(seperator);
195+
};
195196
}

0 commit comments

Comments
 (0)