Extract Last Names (Full Name with Suffix)

Some last names in our directory of faculty end with suffixes. This adds a level of complexity to extracting and sorting by last name without a custom field. A custom JavaScript function could be constructed to handle this, provided you know all the possible suffix variations.

Revised production code

Original Code

 1function getLastName(fullName) {
 2    // A set provides faster lookups than an array for the suffixes.
 3    const suffixes = new Set(['jr', 'sr', 'i', 'ii', 'iii', 'iv', 'esq', 'jd', 'llm']);
 4
 5    // Trim whitespace and remove any commas or periods.
 6    // Cases like "John Doe, Esq." => .
 7    const normalizedName = fullName.replace(/\,|\./gm, '').trim();
 8
 9    const nameParts = normalizedName.split(' ');
10
11    const lastWord = nameParts[nameParts.length - 1];
12    // Handle suffix cases
13    if (suffixes.has(lastWord.toLowerCase())) {
14        return nameParts[nameParts.length - 2];
15    }
16    // Handle no suffix cases
17    return lastWord;
18}

Should it be useful, here is the PHP equivalent:

 1<?php
 2function getLastName(string $fullName): string
 3{
 4    // can be used, with `in_array` or `array_search` for lookup.
 5    $suffixes = ['jr', 'sr', 'i', 'ii', 'iii', 'iv', 'esq', 'jd', 'llm'];
 6
 7    // Trim whitespace and remove any commas or periods.
 8    $normalizedName = trim(str_replace(['.', ','], '', $fullName));
 9
10    $nameParts = explode(' ', $normalizedName);
11
12    // Get the last word for comparison.
13    $lastWord = end($nameParts);
14    $lastWord = strtolower($lastWord);
15    // Handle suffix cases
16    if (in_array($lastWord, $suffixes)) {
17        // Find the second-to-last element.
18        return $nameParts[count($nameParts) - 2];
19    }
20    // Handle no suffix cases
21    return end($nameParts);
22}

Revised Production Code

This was partially written by another developer, which is why it looks quite different from my examples above. I revised it later to account for surname prefixes, for last names starting with De, Van, Le, Di, Da, Mc, Mac. (De Sanctis was sorting as “S” rather than “D”). It doesn’t seem the HONORIFICS constant is providing anything here, and could be refactored out of this now that I look at it again. I’m not sure why the original developer added that since first names aren’t needed for the sort. (2026-6-5)

 1const HONORIFICS = ["mr", "mrs", "ms", "miss", "mx", "dr", "prof", "professor", "hon", "judge", "justice", "dean"];
 2const SUFFIXES = ["jr", "sr", "ii", "iii", "iv", "esq", "esquire"];
 3const SURNAME_PREFIXES = ["de", "van", "le", "di", "da", "mc", "mac"];
 4
 5function normalizeName(s) {
 6    return (s || "").toString().replace(/\s+/g, " ").trim();
 7}
 8function lastNameForSort(fullName) {
 9    if (!fullName) return "";
10    let s = normalizeName(fullName);
11    const re = new RegExp(`^(${HONORIFICS.join("|")})\\.?\\s+`, "i");
12    s = s.replace(re, "");
13    s = s.split(",")[0];
14    let parts = s.split(" ").filter(Boolean);
15    while (parts.length > 1) {
16        const last = parts[parts.length - 1].replace(/\./g, "").toLowerCase();
17        if (SUFFIXES.includes(last)) parts.pop();
18        else break;
19    }
20    if (parts.length > 0) {
21        let lastName = parts[parts.length - 1];
22        // Check the word before the last one for a common prefix
23        if (parts.length > 1) {
24        const prefix = parts[parts.length - 2].toLowerCase();
25        if (SURNAME_PREFIXES.includes(prefix)) {
26            lastName = `${prefix} ${lastName}`;
27        }
28        }
29        return lastName.toLowerCase();
30    }
31
32    return "";
33}
34function compareByLast(a, b) {
35    return a._lastName.localeCompare(b._lastName);
36}

tags: regexjavascriptcodephpwork