NYSIIS: Difference between revisions

From Rosetta Code
Content deleted Content added
Thundergnat (talk | contribs)
m →‎{{header|Perl 6}}: improved the last substitution
→‎Using a library: Added additional test case, fixed duplicate import
 
(85 intermediate revisions by 21 users not shown)
Line 1: Line 1:
{{task|text processing}}
{{draft task|text processing}}
{{wikipedia}}
{{wikipedia}}

The [[wp:New York State Identification and Intelligence System|New York State Identification and Intelligence System phonetic code]], commonly known as NYSIIS, is a phonetic algorithm for creating indices for words based on their pronunciation. The goal is for homophones to be encoded to the same representation so that they can be matched despite minor differences in spelling.

The [[wp:New York State Identification and Intelligence System|New York State Identification and Intelligence System phonetic code]], commonly known as NYSIIS, is a phonetic algorithm for creating indices for words based on their pronunciation.

The goal is for homophones to be encoded to the same representation so that they can be matched despite minor differences in spelling.


;Task:
Implement the original NYSIIS algorithm, shown in Wikipedia, rather than any other subsequent modification.

Also, before the algorithm is applied the input string should be converted to upper case with all white space removed.

An optional step is to handle multiple names, including double-barrelled names or double surnames (e.g. 'Hoyle-Johnson' or 'Vaughan Williams') and unnecessary suffixes/honours that are not required for indexing purposes (e.g. 'Jnr', 'Sr', 'III', etc) - a small selection will suffice.

The original implementation is also restricted to six characters, but this is not a requirement.


;Note
rules 7, 8 and 9 in the Wikipedia article are not mutually exclusive, Willis encodes to WAL, for example.<br/>

;See also
;See also
* [[Soundex]]
* [[Soundex]]
<br><br>


=={{header|Caché ObjectScript}}==
=={{header|11l}}==
{{trans|Python}}
<lang cache>Class Utils.Phonetic [ Abstract ]
{{incorrect|11l|Willis should encode to WAL and Mathews to MAT - see the discussion page}}

<syntaxhighlight lang="11l">V _vowels = ‘AEIOU’

F replace_at(String text; position, fromlist, tolist)
L(f, t) zip(fromlist, tolist)
I text[position .+ f.len] == f
R text[0 .< position]‘’t‘’text[position + f.len ..]
R text

F replace_end(String text; fromlist, tolist)
L(f, t) zip(fromlist, tolist)
I text.ends_with(f)
R text[0 .< (len)-f.len]‘’t
R text

F nysiis(String =name)
name = name.replace(re:‘\W’, ‘’).uppercase()
name = replace_at(name, 0, [‘MAC’, ‘KN’, ‘K’, ‘PH’, ‘PF’, ‘SCH’],
[‘MCC’, ‘N’, ‘C’, ‘FF’, ‘FF’, ‘SSS’])
name = replace_end(name, [‘EE’, ‘IE’, ‘DT’, ‘RT’, ‘RD’, ‘NT’, ‘ND’],
[‘Y’, ‘Y’, ‘D’, ‘D’, ‘D’, ‘D’, ‘D’])
V key = String(name[0])
V key1 = ‘’
V i = 1
L i < name.len
V (n_1, n) = (name[i - 1], name[i])
V n1_ = I i + 1 < name.len {name[i + 1]} E ‘’
name = replace_at(name, i, [‘EV’] [+] Array(:_vowels).map(String), [‘AF’] [+] [String(‘A’)] * 5)
name = replace_at(name, i, [String(‘Q’), ‘Z’, ‘M’], [String(‘G’), ‘S’, ‘N’])
name = replace_at(name, i, [‘KN’, ‘K’], [String(‘N’), ‘C’])
name = replace_at(name, i, [‘SCH’, ‘PH’], [‘SSS’, ‘FF’])
I n == ‘H’ & (n_1 !C :_vowels | n1_ !C :_vowels)
name = name[0 .< i]‘’n_1‘’name[i + 1 ..]
I n == ‘W’ & n_1 C :_vowels
name = name[0 .< i]‘A’name[i + 1 ..]
I key != ‘’ & key.last != name[i]
key ‘’= name[i]
i++
key = replace_end(key, [‘S’, ‘AY’, ‘A’], [‘’, ‘Y’, ‘’])
R key1‘’key

V names = [‘Bishop’, ‘Carlson’, ‘Carr’, ‘Chapman’, ‘Franklin’,
‘Greene’, ‘Harper’, ‘Jacobs’, ‘Larson’, ‘Lawrence’,
‘Lawson’, ‘Louis, XVI’, ‘Lynch’, ‘Mackenzie’, ‘Matthews’,
‘McCormack’, ‘McDaniel’, ‘McDonald’, ‘Mclaughlin’, ‘Morrison’,
‘O'Banion’, ‘O'Brien’, ‘Richards’, ‘Silva’, ‘Watkins’,
‘Wheeler’, ‘Willis’, ‘brown, sr’, ‘browne, III’, ‘browne, IV’,
‘knight’, ‘mitchell’, ‘o'daniel’]
L(name) names
print(‘#15: #.’.format(name, nysiis(name)))</syntaxhighlight>

{{out}}
<pre style="height:15em;">
Bishop: BASAP
Carlson: CARLSAN
Carr: CAR
Chapman: CAPNAN
Franklin: FRANCLAN
Greene: GRAN
Harper: HARPAR
Jacobs: JACAB
Larson: LARSAN
Lawrence: LARANC
Lawson: LASAN
Louis, XVI: LASXV
Lynch: LYNC
Mackenzie: MCANSY
Matthews: MATA
McCormack: MCARNAC
McDaniel: MCDANAL
McDonald: MCDANALD
Mclaughlin: MCLAGLAN
Morrison: MARASAN
O'Banion: OBANAN
O'Brien: OBRAN
Richards: RACARD
Silva: SALV
Watkins: WATCAN
Wheeler: WALAR
Willis: WALA
brown, sr: BRANSR
browne, III: BRAN
browne, IV: BRANAV
knight: NAGT
mitchell: MATCAL
o'daniel: ODANAL
</pre>

=={{header|ALGOL 68}}==
This produces the same results as the Apache Commons Nysiis codec, so Mathews yields MAT and Willis yields WAL.<br/> <syntaxhighlight lang="algol68">
BEGIN # NYSIIS algorithm #

# returns str in upper-case with non-letters removed #
OP TOALPHAUPPER = ( STRING str )STRING:
BEGIN
STRING result := "";
FOR i FROM LWB str TO UPB str DO
CHAR c = str[ i ];
IF c >= "a" AND c <= "z"
THEN result +:= REPR ( ABS c + ( ABS "A" - ABS "a" ) )
ELIF c >= "A" AND c <= "Z"
THEN result +:= c
FI
OD;
result
END # TOALPHAUPPER # ;
# returns str with all characters in delete removed #
PRIO REMOVE = 1;
OP REMOVE = ( STRING str, CHAR delete )STRING:
BEGIN
STRING result := "";
FOR i FROM LWB str TO UPB str DO
CHAR c = str[ i ];
IF c /= delete THEN
result +:= c
FI
OD;
result
END # REMOVE # ;
# returns str with with the character at pos removed #
OP REMOVE = ( STRING str, INT pos )STRING:
IF pos < LWB str OR pos > UPB str
THEN str
ELIF pos = LWB str
THEN str[ LWB str + 1 : ]
ELIF pos = UPB str
THEN str[ : LENGTH str - 1 ]
ELSE str[ : pos - 1 ] + str[ pos + 1 : ]
FI # REMOVE # ;
# returns the length of str #
OP LENGTH = ( STRING str )INT: ( UPB str - LWB str ) + 1;
# returns TRUE is str starts with prefix, FALSE otherwise #
PRIO STARTSWITH = 9;
OP STARTSWITH = ( STRING str, prefix )BOOL:
IF INT p len = LENGTH prefix;
p len > LENGTH str
THEN FALSE
ELSE str[ LWB str : LWB str + p len - 1 ] = prefix
FI # STARTSWITH # ;
OP STARTSWITH = ( STRING str, CHAR prefix )BOOL: str STARTSWITH STRING(prefix);
# returns TRUE if c is a vowel (AEIOU), FALSE otherwise #
OP VOWEL = ( CHAR c )BOOL: c = "A" OR c = "E" OR c = "I" OR c = "O" OR c = "U";
# returns the character from str following pos, if there is one, #
# REPR 0 otherwise #
PRIO FOLLOWING = 9;
OP FOLLOWING = ( STRING str, INT pos )CHAR: IF pos < UPB str THEN str[ pos + 1 ] ELSE REPR 0 FI;
# left pads str to at least w charaters #
PRIO PAD = 9;
OP PAD = ( INT w, STRING str )STRING:
IF LENGTH str >= w THEN str ELSE ( ( w - LENGTH str ) * " " ) + str FI;
# returns the NYSIIS code of name #
OP NYSIIS = ( STRING name )STRING:
BEGIN
STRING cname := ( TOALPHAUPPER ( name REMOVE " " ) );
IF cname STARTSWITH "MAC" THEN cname[ 1 : 3 ] := "MCC"
ELIF cname STARTSWITH "KN" THEN cname[ 1 : 2 ] := "NN"
ELIF cname STARTSWITH "K" THEN cname[ 1 ] := "C"
ELIF cname STARTSWITH "PH" THEN cname[ 1 : 2 ] := "FF"
ELIF cname STARTSWITH "PF" THEN cname[ 1 : 2 ] := "FF"
ELIF cname STARTSWITH "SCH" THEN cname[ 1 : 3 ] := "SSS"
FI;
INT r len := LENGTH cname;
STRING suffix = cname[ r len - 1 : r len ];
IF suffix = "EE" OR suffix = "IE" THEN cname[ r len - 1 : ] := "Y "
ELIF suffix = "DT" OR suffix = "RT" OR suffix = "RD"
OR suffix = "NT" OR suffix = "ND" THEN cname[ r len - 1 : ] := "D "
FI;
INT r pos := 2;
WHILE IF r pos > r len THEN FALSE ELSE cname[ r pos ] /= " " FI DO
CHAR p = cname[ r pos - 1 ];
CHAR c = cname[ r pos ];
CHAR n = cname FOLLOWING r pos;
IF c = "E" THEN
IF n = "V" THEN cname[ r pos : r pos + 1 ] := "AF" ELSE cname[ r pos ] := "A" FI
ELIF VOWEL c THEN cname[ r pos ] := "A"
ELIF c = "Q" THEN cname[ r pos ] := "G"
ELIF c = "Z" THEN cname[ r pos ] := "S"
ELIF c = "M" THEN cname[ r pos ] := "N"
ELIF IF r pos >= r len - 1 THEN FALSE ELSE cname[ r pos : r pos + 2 ] = "SCH" FI
THEN cname[ r pos : r pos + 2 ] := "SSS"
ELIF c = "P" AND n = "H" THEN cname[ r pos : r pos + 1 ] := "FF"
ELIF c = "K" THEN cname[ r pos ] := IF n = "N" THEN "N" ELSE "C" FI
ELIF c = "H"
AND ( NOT VOWEL p OR NOT VOWEL n ) THEN cname[ r pos ] := p
ELIF c = "W" AND VOWEL p
THEN cname[ r pos ] := p
FI;
IF cname[ r pos ] = p
THEN cname := cname REMOVE r pos; r len -:= 1
ELSE r pos +:= 1
FI
OD;
IF cname[ r len ] = " " THEN cname := cname REMOVE r len; r len -:= 1 FI;
IF IF r len < 1 THEN FALSE ELSE c name[ r len ] = "S" FI
THEN cname := cname REMOVE r len;
r len -:= 1
FI;
IF IF r len < 2 THEN FALSE ELSE c name[ r len - 1 : ] = "AY" FI
THEN cname := cname REMOVE r len;
cname[ r len -:= 1 ] := "Y"
FI;
IF IF r len < 1 THEN FALSE ELSE c name[ r len ] = "A" FI
THEN cname := cname REMOVE r len;
r len -:= 1
FI;
cname
END # NYSIIS # ;

# test cases as used in other samples, e.g. 11l #
[,]STRING tests = ( ( "Bishop", "BASAP" )
, ( "Carlson", "CARLSAN" )
, ( "Carr", "CAR" )
, ( "Chapman", "CAPNAN" )
, ( "Franklin", "FRANCLAN" )
, ( "Greene", "GRAN" )
, ( "Harper", "HARPAR" )
, ( "Jacobs", "JACAB" )
, ( "Larson", "LARSAN" )
, ( "Lawrence", "LARANC" )
, ( "Lawson", "LASAN" )
, ( "Louis, XVI", "LASXV" )
, ( "Lynch", "LYNC" )
, ( "Mackenzie", "MCANSY" )
, ( "Matthews", "MAT" )
, ( "McCormack", "MCARNAC" )
, ( "McDaniel", "MCDANAL" )
, ( "McDonald", "MCDANALD" )
, ( "Mclaughlin", "MCLAGLAN" )
, ( "Morrison", "MARASAN" )
, ( "O'Banion", "OBANAN" )
, ( "O'Brien", "OBRAN" )
, ( "Richards", "RACARD" )
, ( "Silva", "SALV" )
, ( "Watkins", "WATCAN" )
, ( "Wheeler", "WALAR" )
, ( "Willis", "WAL" )
, ( "brown, sr", "BRANSR" )
, ( "browne, III", "BRAN" )
, ( "browne, IV", "BRANAV" )
, ( "hayes", "HAY" )
, ( "knight", "NAGT" )
, ( "mitchell", "MATCAL" )
, ( "o'daniel", "ODANAL" )
);
FOR t FROM 1 LWB tests TO 1 UPB tests DO
STRING name = tests[ t, 1 ];
STRING code = NYSIIS name;
STRING expected = tests[ t, 2 ];
print( ( 12 PAD name, " -> ", code
, IF code = expected
THEN ""
ELSE " !! expected " + expected
FI
, newline
)
)
OD
END
</syntaxhighlight>
{{out}}
<pre style="height:15em">
Bishop -> BASAP
Carlson -> CARLSAN
Carr -> CAR
Chapman -> CAPNAN
Franklin -> FRANCLAN
Greene -> GRAN
Harper -> HARPAR
Jacobs -> JACAB
Larson -> LARSAN
Lawrence -> LARANC
Lawson -> LASAN
Louis, XVI -> LASXV
Lynch -> LYNC
Mackenzie -> MCANSY
Matthews -> MAT
McCormack -> MCARNAC
McDaniel -> MCDANAL
McDonald -> MCDANALD
Mclaughlin -> MCLAGLAN
Morrison -> MARASAN
O'Banion -> OBANAN
O'Brien -> OBRAN
Richards -> RACARD
Silva -> SALV
Watkins -> WATCAN
Wheeler -> WALAR
Willis -> WAL
brown, sr -> BRANSR
browne, III -> BRAN
browne, IV -> BRANAV
hayes -> HAY
knight -> NAGT
mitchell -> MATCAL
o'daniel -> ODANAL
</pre>

=={{header|C++}}==
Implementation based on Wikipedia description of the algorithm.
<syntaxhighlight lang="c">
#include <iostream> // required for debug code in main() only
#include <iomanip> // required for debug code in main() only
#include <string>
#include <cstring>

std::string NYSIIS( std::string const& str )
{
{
std::string s, out;
s.reserve( str.length() );
for( auto const c : str )
{
if( c >= 'a' && c <= 'z' )
s += c - ('a' - 'A');
else if( c >= 'A' && c <= 'Z' )
s += c;
}


auto replace = []( char const * const from, char const* to, char* const dst ) -> bool
ClassMethod UnitTest() As %Status
{
auto const n = strlen( from );
if( strncmp( from, dst, n ) == 0 )
{
strncpy( dst, to, n );
return true;
}
return false;
};

auto multiReplace = []( char const* const* from, char const* to, char* const dst ) -> bool
{
auto const n = strlen( *from );
for( ; *from; ++from )
if( strncmp( *from, dst, n ) == 0 )
{
memcpy( dst, to, n );
return true;
}
return false;
};

auto isVowel = []( char const c ) -> bool
{
return c == 'A' || c == 'E' || c == 'I' || c == 'O' || c == 'U';
};

size_t n = s.length();
replace( "MAC", "MCC", &s[0] );
replace( "KN", "NN", &s[0] );
replace( "K", "C", &s[0] );
char const* const prefix[] = { "PH", "PF", 0 };
multiReplace( prefix, "FF", &s[0] );
replace( "SCH", "SSS", &s[0] );

char const* const suffix1[] = { "EE", "IE", 0 };
char const* const suffix2[] = { "DT", "RT", "RD", "NT", "ND", 0 };
if( multiReplace( suffix1, "Y", &s[n - 2] ) || multiReplace( suffix2, "D", &s[n - 2] ))
{
s.pop_back();
--n;
}

out += s[0];

char* vowels[] = { "A", "E", "I", "O", "U", 0 };
for( unsigned i = 1; i < n; ++i )
{
char* const c = &s[i];
if( !replace( "EV", "AV", c ) )
multiReplace( vowels, "A", c );
replace( "Q", "G", c );
replace( "Z", "S", c );
replace( "M", "N", c );
if( !replace( "KN", "NN", c ))
replace( "K", "C", c );
replace( "SCH", "SSS", c );
replace( "PH", "FF", c );
if( *c == 'H' && (!isVowel( s[i - 1] ) || i + 1 >= n || !isVowel( s[i + 1] )))
*c = s[i - 1];
if( *c == 'W' && isVowel( s[i - 1] ))
*c = 'A';
if( out.back() != *c )
out += *c;
}

if( out.back() == 'S')
out.pop_back();
n = out.length() - 2;
if( out[n] == 'A' && out[n + 1] == 'Y' )
out = out.substr( 0, n ) + "Y";
if( out.back() == 'A' )
out.pop_back();
return out;
}

int main()
{
{
static char const * const names[][2] = {
// define test list
{ "Bishop", "BASAP" },
Set testlist=$ListBuild(
{ "Carlson", "CARLSAN" },
$ListBuild("knight", "NAGT"),
{ "Carr", "CAR" },
$ListBuild("mitchell", "MATCAL"),
{ "Chapman", "CAPNAN" },
$ListBuild("o'daniel", "ODANAL"),
{ "Franklin", "FRANCLAN" },
$ListBuild("brown sr", "BRAN"),
{ "Greene", "GRAN" },
$ListBuild("browne III", "BRAN"),
{ "Harper", "HARPAR" },
$ListBuild("browne IV", "BRAN"),
{ "Jacobs", "JACAB" },
$ListBuild("O'Banion", "OBANAN"),
{ "Larson", "LARSAN" },
$ListBuild("Mclaughlin", "MCLAGL[AN]"),
{ "Lawrence", "LARANC" },
$ListBuild("McCormack", "MCARNA[C]"),
{ "Lawson", "LASAN" },
$ListBuild("Chapman", "CHAPNA[N]"),
{ "Louis, XVI", "LASXV" },
$ListBuild("Silva", "SALV"),
{ "Lynch", "LYNC" },
$ListBuild("McDonald", "MCDANA[LD]"),
{ "Mackenzie", "MCANSY" },
$ListBuild("Lawson", "LASAN"),
{ "Matthews", "MAT" },
$ListBuild("Jacobs", "JACAB"),
{ "McCormack", "MCARNAC" },
$ListBuild("Greene", "GRAN"),
{ "McDaniel", "MCDANAL" },
$ListBuild("O'Brien", "OBRAN"),
{ "McDonald", "MCDANALD" },
$ListBuild("Morrison", "MARASA[N]"),
{ "Mclaughlin", "MCLAGLAN" },
$ListBuild("Larson", "LARSAN"),
{ "Morrison", "MARASAN" },
$ListBuild("Willis", "WAL"),
{ "O'Banion", "OBANAN" },
$ListBuild("Mackenzie", "MCANSY"),
{ "O'Brien", "OBRAN" },
$ListBuild("Carr", "CAR"),
{ "Richards", "RACARD" },
$ListBuild("Lawrence", "LARANC"),
{ "Silva", "SALV" },
$ListBuild("Matthews", "MAT"),
{ "Watkins", "WATCAN" },
$ListBuild("Richards", "RACARD"),
{ "Wheeler", "WALAR" },
$ListBuild("Bishop", "BASAP"),
{ "Willis", "WAL" },
$ListBuild("Franklin", "FRANCL[AN]"),
{ "brown, sr", "BRANSR" },
$ListBuild("McDaniel", "MCDANA[L]"),
{ "browne, III", "BRAN" },
$ListBuild("Harper", "HARPAR"),
{ "browne, IV", "BRANAV" },
$ListBuild("Lynch", "LYNC"),
{ "knight", "NAGT" },
$ListBuild("Watkins", "WATCAN"),
{ "mitchell", "MATCAL" },
$ListBuild("Carlson", "CARLSA[N]"),
{ "o'daniel", "ODANAL" } };
$ListBuild("Wheeler", "WHALAR")

)
for( auto const& name : names )
{
// order through test list
auto const code = NYSIIS( name[0] );
Set ptr=0, sc=$$$ERROR($$$GeneralError, "No entries found.")
std::cout << std::left << std::setw( 16 ) << name[0] << std::setw( 8 ) << code;
While $ListNext(testlist, ptr, val) {
if( code == std::string( name[1] ))
Set sc=##class(Utils.Phonetic).Encode("nysiis", $List(val), .code,, 6)
std::cout << " ok";
If $$$ISERR(sc) Quit
else
If code'=$List(val, 2) Set sc=$$$ERROR($$$GeneralError, "Encoding did not match.") Quit
std::cout << " ERROR: " << name[1] << " expected";
}
std::cout << std::endl;
}
// finished

If $$$ISERR(sc) Quit sc
return 0;
Quit $$$OK
}
}
</syntaxhighlight>
{{out|Example output}}
<pre style="height:15em;">
Bishop BASAP ok
Carlson CARLSAN ok
Carr CAR ok
Chapman CAPNAN ok
Franklin FRANCLAN ok
Greene GRAN ok
Harper HARPAR ok
Jacobs JACAB ok
Larson LARSAN ok
Lawrence LARANC ok
Lawson LASAN ok
Louis, XVI LASXV ok
Lynch LYNC ok
Mackenzie MCANSY ok
Matthews MAT ok
McCormack MCARNAC ok
McDaniel MCDANAL ok
McDonald MCDANALD ok
Mclaughlin MCLAGLAN ok
Morrison MARASAN ok
O'Banion OBANAN ok
O'Brien OBRAN ok
Richards RACARD ok
Silva SALV ok
Watkins WATCAN ok
Wheeler WALAR ok
Willis WAL ok
brown, sr BRANSR ok
browne, III BRAN ok
browne, IV BRANAV ok
knight NAGT ok
mitchell MATCAL ok
o'daniel ODANAL ok
</pre>

=={{header|Caché ObjectScript}}==
Refactored code based on other examples to reduce footprint.


<syntaxhighlight lang="cos">
ClassMethod Encode(pAlgorithm As %String = "", pName As %String = "", ByRef pCode As %String, pSuffixRem As %Boolean = 1, pTruncate As %Integer = 0) As %Status
Class Utils.Phonetic [ Abstract ]
{
{

// check algorithm
ClassMethod EncodeName(pAlgorithm As %String = "", pName As %String = "", Output pCode As %String, pSuffixRem As %Boolean = 1, pTruncate As %Integer = 0) As %Status
{
// check algorithm and name
Set pAlgorithm=$ZConvert(pAlgorithm, "l")
Set pAlgorithm=$ZConvert(pAlgorithm, "l")
If pAlgorithm="" Quit $$$ERROR($$$GeneralError, "No algorithm specified.")
If pAlgorithm="" Quit $$$ERROR($$$GeneralError, "No algorithm specified.")
If $Case(pAlgorithm, "nysiis":1, :0)=0 Quit $$$ERROR($$$GeneralError, "Unknown algorithm specified.")
If $Case(pAlgorithm, "nysiis":1, :0)=0 Quit $$$ERROR($$$GeneralError, "Unknown algorithm specified.")
// check name
If $Match(pName, ".*\d.*# no numbers") Quit $$$ERROR($$$GeneralError, "Name cannot contain numerics.")
If $Match(pName, ".*\d.*# no numbers") Quit $$$ERROR($$$GeneralError, "Name cannot contain numerics.")
// remove apostrophes
// remove apostrophes, find punctuation and replace with spaces (exclude hyphens)
Set pName=$Translate(pName, "'")
Set pName=$Translate(pName, "'")
// find punctuation and replace with spaces (exclude hyphens)
Set pun=$ZStrip(pName, "*E'P", "-")
Set pun=$ZStrip(pName, "*E'P", "-")
Set pName=$Translate(pName, pun, $Justify(" ", $Length(pun)))
Set pName=$Translate(pName, pun, $Justify(" ", $Length(pun)))
Line 83: Line 537:
// - http://en.wikipedia.org/wiki/List_of_post-nominal_letters_(United_Kingdom)
// - http://en.wikipedia.org/wiki/List_of_post-nominal_letters_(United_Kingdom)
If pSuffixRem {
If pSuffixRem {
Set ords=$ListBuild("KG", "LG", "KT", "LT", "GCB", "KCB", "DCB", "CB", "GCMG", "KCMG", "DCMG", "CMG", "DSO", "GCVO",
Set ords=$ListBuild("KG", "LG", "KT", "LT", "GCB", "KCB", "DCB", "CB", "GCMG", "KCMG", "DCMG", "CMG", "DSO",
"KCVO", "DCVO", "CVO", "LVO", "MVO", "OM", "ISO", "GBE", "KBE", "DBE", "CBE", "OBE", "MBE", "CH")
"GCVO", "KCVO", "DCVO", "CVO", "LVO", "MVO", "OM", "ISO", "GBE", "KBE", "DBE", "CBE", "OBE", "MBE", "CH")
Set decs=$ListBuild("VC", "GC", "CGC", "RRC", "DSC", "MC", "DFC", "AFC", "ARRC", "OBI", "IOM")
Set decs=$ListBuild("VC", "GC", "CGC", "RRC", "DSC", "MC", "DFC", "AFC", "ARRC", "OBI", "IOM")
Set regexp="( )(SNR$|SR$|JNR$|JR$|ESQ$|"_$ListToString(ords, "$|")_"$|"_$ListToString(decs, "$|")_"$|[IV]+$)"
Set regexp="( )(SNR|SR|JNR|JR|ESQ|"_$ListToString(ords, "|")_"|"_$ListToString(decs, "|")_"|[IVX]+)"
Set rem=##class(%Regex.Matcher).%New(regexp, pName)
For {
Set pName=rem.ReplaceAll("")
Set locn=$Locate(pName, regexp) If 'locn Quit
Set pName=$Extract(pName, 1, locn-1)
}
}
}
// lastly replace hyphen with space
// replace hyphen and white space, plus some final validation
Set pName=$ZStrip($Translate(pName, "-", " "), "<=>W")
Set pName=$ZStrip($Translate(pName, "-", " "), "<=>W")
// some final validation
If $Length($Piece(pName, " "))<2 Quit $$$ERROR($$$GeneralError, "Invalid name.")
If $Length($Piece(pName, " "))<2 Quit $$$ERROR($$$GeneralError, "Invalid name.")
// begin algorithm
// begin algorithm and truncate result, if necessary
Set pCode=""
Set pCode=""
For piece=1:1:$Length(pName, " ") {
For piece=1:1:$Length(pName, " ") {
If pAlgorithm="nysiis" Set pCode=pCode_..NYSIIS(pName)
If pAlgorithm="nysiis" Set pCode=pCode_..ToNYSIIS($Piece(pName, " ", piece))
}
}
// truncate string, if necessary
If pTruncate {
If pTruncate {
Set pName=pCode
Set pName=pCode
Line 117: Line 565:
}
}


ClassMethod NYSIIS(pName As %String) As %String
ClassMethod ToNYSIIS(pName As %String) As %String
{
{
/*
/*
Line 124: Line 572:
- http://www.dropby.com/indexLF.html?content=/NYSIIS.html
- http://www.dropby.com/indexLF.html?content=/NYSIIS.html
*/
*/
// create regexp matcher instance, remove punctuation and convert all to upper case
Set rem=##class(%Regex.Matcher).%New(" ")
Set rem.Text=$ZConvert($ZStrip(pName, "*P"), "U")
// translate first characters of name:
// translate first characters of name:
// => MAC->MCC, KN->N, K->C, PH/PF->FF, SCH->SSS
// => MAC->MCC, KN->N, K->C, PH/PF->FF, SCH->SSS
For rule="^MAC->MCC", "^KN->N", "^K->C", "^(PH|PF)->FF", "^SCH->SSS" {
Set pName1=$Extract(pName, 1)
Set pName2=$Extract(pName, 1, 2)
Set rem.Pattern=$Piece(rule, "->")
If rem.Locate() Set rem.Text=rem.ReplaceFirst($Piece(rule, "->", 2)) Quit
Set pName3=$Extract(pName, 1, 3)
If pName3="MAC" {
Set $Extract(pName, 1, 3)="MCC"
} ElseIf pName2="KN" {
Set $Extract(pName, 1, 2)="N"
} ElseIf pName1="K" {
Set $Extract(pName, 1)="C"
} ElseIf pName2="PH" {
Set $Extract(pName, 1, 2)="FF"
} ElseIf pName2="PF" {
Set $Extract(pName, 1, 2)="FF"
} ElseIf pName3="SCH" {
Set $Extract(pName, 1, 3)="SSS"
}
}

// translate last characters of name:
// translate last characters of name:
// => EE/IE->Y, DT/RT/RD/NT/ND->D
// => EE/IE->Y, DT/RT/RD/NT/ND->D
For rule="(EE|IE)$->Y", "(DT|RT|RD|NT|ND)$->D" {
Set pNamexx=$Case($Extract(pName, *-1, *), "EE": "Y", "IE": "Y",
Set rem.Pattern=$Piece(rule, "->")
"DT": "D", "RT": "D", "RD": "D", "NT": "D", "ND": "D", :"")
If $Length(pNamexx) Set pName=$Extract(pName, 1, *-2)_pNamexx
If rem.Locate() Set rem.Text=rem.ReplaceFirst($Piece(rule, "->", 2)) Quit
}
// first character of key = first character of name
// first character of key = first character of name
Set pName1=$Extract(pName, 1)
Set pName1=$Extract(rem.Text, 1), rem.Text=$Extract(rem.Text, 2, *)
Set $Extract(pName, 1)=""
// translate remaining characters by following rules, incrementing by one character each time:
// translate remaining characters by following rules, incrementing by one character each time:
Line 162: Line 602:
// => W->if previous is vowel, A (A is the only vowel left)
// => W->if previous is vowel, A (A is the only vowel left)
// => add current to key if current is not same as the last key character
// => add current to key if current is not same as the last key character
Set ptr=0, rules=$ListBuild("EV->AF", "(A|E|I|O|U)->A", "Q->G", "Z->S", "M->N", "KN->N", "K->C",
Set pName=$Replace(pName, "EV", "AF")
"SCH->SSS", "PH->FF", "H[^A]", "[^A]H", "AW->A")
Set pName=$Translate(pName, "EIOU", "AAAA")
While $ListNext(rules, ptr, rule) {
Set pName=$Translate(pName, "QZM", "GSN")
Set pName=$Replace(pName, "KN", "N")
Set rem.Pattern=$Piece(rule, "->")
Set pName=$Translate(pName, "K", "C")
If $Piece(rule, "->", 2)="", rem.Locate() {
Set pName=$Replace(pName, "SCH", "SSS")
Set $Piece(rule, "->", 2)=$Translate(rem.Group, "H")
}
Set pName=$Replace(pName, "PH", "FF")
Set rem.Text=rem.ReplaceAll($Piece(rule, "->", 2))
Set locn=$Locate(pName, "H[^A]") If locn Set $Extract(pName, locn)=""
}
Set locn=$Locate(pName, "[^A]H") If locn Set $Extract(pName, locn+1)=""
Set pName=$Replace(pName, "AW", "A")
Set pName=$ZStrip(rem.Text, "=U") // remove duplicates
Set pName=$ZStrip(pName,"=U") // remove duplicates
// if last character is S, remove it
// if last character is S, remove it
Line 187: Line 626:
}
}


}
}</lang>
</syntaxhighlight>
{{out|Examples}}
{{out|Examples}}
<pre>
<pre>
USER>Do ##class(Utils.Phonetic).Encode("nysiis", "Mclaughlin", .code)
USER>For { Read !, name Quit:name="" Set sc=##class(Utils.Phonetic).EncodeName("nysiis", name, .code,, 6) If sc Write " -> ", code }

USER>Write code
knight -> NAGT
MCLAGLAN
mitchell -> MATCAL
o'daniel -> ODANAL
brown sr -> BRAN
browne III -> BRAN
browne IV -> BRAN
O'Banion -> OBANAN
Mclaughlin -> MCLAGL[AN]
McCormack -> MCARNA[C]
Chapman -> CHAPNA[N]
Silva -> SALV
McDonald -> MCDANA[LD]
Lawson -> LASAN
Jacobs -> JACAB
Greene -> GRAN
O'Brien -> OBRAN
Morrison -> MARASA[N]
Larson -> LARSAN
Willis -> WAL
Mackenzie -> MCANSY
Carr -> CAR
Lawrence -> LARANC
Matthews -> MAT
Richards -> RACARD
Bishop -> BASAP
Franklin -> FRANCL[AN]
McDaniel -> MCDANA[L]
Harper -> HARPAR
Lynch -> LYNC
Watkins -> WATCAN
Carlson -> CARLSA[N]
Wheeler -> WHALAR
Louis XVI -> L
Hoyle-Johnson -> HAYLJA[NSAN]
Vaughan Williams -> VAGANW[ALAN]
D'Souza -> DSAS
de Sousa -> DSAS
</pre>
</pre>


=={{header|D}}==
{{trans|Python}}
<syntaxhighlight lang="d">import std.stdio, std.regex, std.algorithm, std.range, std.string;

string replaceAt(in string text, in uint pos, in string[] fromList,
in string[] toList) pure /*nothrow*/ @safe {
foreach (const f, const t; zip(fromList, toList))
if (text[pos .. $].startsWith(f))
return [text[0 .. pos], t, text[pos + f.length .. $]].join;
return text;
}

string replaceEnd(in string text, in string[] fromList,
in string[] toList) pure /*nothrow*/ @safe {
foreach (const f, const t; zip(fromList, toList))
if (text.endsWith(f))
return text[0 .. $ - f.length] ~ t;
return text;
}

string nysiis(string name) /*pure nothrow*/ @safe {
enum vowels = "AEIOU";
name = name.replaceAll(r"\W".regex, "").toUpper;
name = name.replaceAt(0, ["MAC", "KN", "K", "PH", "PF", "SCH"],
["MCC", "N", "C", "FF", "FF", "SSS"]);
name = name.replaceEnd(["EE", "IE", "DT", "RT", "RD", "NT", "ND"],
["Y", "Y", "D", "D", "D", "D", "D"]);
string key = name[0 .. 1];
string key1;

foreach (immutable i; 1 .. name.length) {
immutable n_1 = name[i - 1 .. i];
immutable n = name[i];
immutable n1b = (i + 1 < name.length) ? name[i+1 .. i+2] : "";
name = name.replaceAt(i, ["EV"] ~ vowels.split(""), ["AF"] ~
["A"].replicate(5));
name = name.replaceAt(i, "QZM".split(""), "GSN".split(""));
name = name.replaceAt(i, ["KN", "K"], ["N", "C"]);
name = name.replaceAt(i, ["SCH", "PH"], ["SSS", "FF"]);
if (n == 'H' && (!vowels.canFind(n_1) || !vowels.canFind(n1b)))
name = [name[0 .. i], n_1, name[i + 1 .. $]].join;
if (n == 'W' && vowels.canFind(n_1))
name = [name[0 .. i], "A", name[i + 1 .. $]].join;
if (!key.empty && key[$ - 1] != name[i])
key ~= name[i];
}

key = replaceEnd(key, ["S"], [""]);
key = replaceEnd(key, ["AY"], ["Y"]);
return key1 ~ replaceEnd(key, ["A"], [""]);
}

void main() @safe {
immutable names = ["Bishop", "Carlson", "Carr", "Chapman",
"Franklin", "Greene", "Harper", "Jacobs", "Larson", "Lawrence",
"Lawson", "Louis, XVI", "Lynch", "Mackenzie", "Matthews",
"McCormack", "McDaniel", "McDonald", "Mclaughlin", "Morrison",
"O'Banion", "O'Brien", "Richards", "Silva", "Watkins",
"Wheeler", "Willis", "brown, sr", "browne, III", "browne, IV",
"knight", "mitchell", "o'daniel"];

foreach (immutable name; names)
writefln("%11s: %s", name, name.nysiis);
}</syntaxhighlight>
{{out}}
<pre style="height:15em;">
Bishop: BASAP
Carlson: CARLSAN
Carr: CAR
Chapman: CAPNAN
Franklin: FRANCLAN
Greene: GRAN
Harper: HARPAR
Jacobs: JACAB
Larson: LARSAN
Lawrence: LARANC
Lawson: LASAN
Louis, XVI: LASXV
Lynch: LYNC
Mackenzie: MCANSY
Matthews: MAT
McCormack: MCARNAC
McDaniel: MCDANAL
McDonald: MCDANALD
Mclaughlin: MCLAGLAN
Morrison: MARASAN
O'Banion: OBANAN
O'Brien: OBRAN
Richards: RACARD
Silva: SALV
Watkins: WATCAN
Wheeler: WALAR
Willis: WAL
brown, sr: BRANSR
browne, III: BRAN
browne, IV: BRANAV
knight: NAGT
mitchell: MATCAL
o'daniel: ODANAL</pre>

=={{header|Go}}==
{{trans|Kotlin}}
<syntaxhighlight lang="go">package main

import (
"fmt"
"strings"
)

type pair struct{ first, second string }

var (
fStrs = []pair{{"MAC", "MCC"}, {"KN", "N"}, {"K", "C"}, {"PH", "FF"},
{"PF", "FF"}, {"SCH", "SSS"}}
lStrs = []pair{{"EE", "Y"}, {"IE", "Y"}, {"DT", "D"}, {"RT", "D"},
{"RD", "D"}, {"NT", "D"}, {"ND", "D"}}
mStrs = []pair{{"EV", "AF"}, {"KN", "N"}, {"SCH", "SSS"}, {"PH", "FF"}}
eStrs = []string{"JR", "JNR", "SR", "SNR"}
)

func isVowel(b byte) bool {
return strings.ContainsRune("AEIOU", rune(b))
}

func isRoman(s string) bool {
if s == "" {
return false
}
for _, r := range s {
if !strings.ContainsRune("IVX", r) {
return false
}
}
return true
}

func nysiis(word string) string {
if word == "" {
return ""
}
w := strings.ToUpper(word)
ww := strings.FieldsFunc(w, func(r rune) bool {
return r == ' ' || r == ','
})
if len(ww) > 1 {
last := ww[len(ww)-1]
if isRoman(last) {
w = w[:len(w)-len(last)]
}
}
for _, c := range " ,'-" {
w = strings.Replace(w, string(c), "", -1)
}
for _, eStr := range eStrs {
if strings.HasSuffix(w, eStr) {
w = w[:len(w)-len(eStr)]
}
}
for _, fStr := range fStrs {
if strings.HasPrefix(w, fStr.first) {
w = strings.Replace(w, fStr.first, fStr.second, 1)
}
}
for _, lStr := range lStrs {
if strings.HasSuffix(w, lStr.first) {
w = w[:len(w)-2] + lStr.second
}
}
initial := w[0]
var key strings.Builder
key.WriteByte(initial)
w = w[1:]
for _, mStr := range mStrs {
w = strings.Replace(w, mStr.first, mStr.second, -1)
}
sb := []byte{initial}
sb = append(sb, w...)
le := len(sb)
for i := 1; i < le; {
switch sb[i] {
case 'E', 'I', 'O', 'U':
sb[i] = 'A'
case 'Q':
sb[i] = 'G'
case 'Z':
sb[i] = 'S'
case 'M':
sb[i] = 'N'
case 'K':
sb[i] = 'C'
case 'H':
if !isVowel(sb[i-1]) || (i < le-1 && !isVowel(sb[i+1])) {
sb[i] = sb[i-1]
}
case 'W':
if isVowel(sb[i-1]) {
sb[i] = 'A'
}
}
if sb[i] == sb[i-1] {
sb = append(sb[:i], sb[i+1:]...)
le--
} else {
i++
}
}
if sb[le-1] == 'S' {
sb = sb[:le-1]
le--
}
if le > 1 && string(sb[le-2:]) == "AY" {
sb = sb[:le-2]
sb = append(sb, 'Y')
le--
}
if le > 0 && sb[le-1] == 'A' {
sb = sb[:le-1]
le--
}
prev := initial
for j := 1; j < le; j++ {
c := sb[j]
if prev != c {
key.WriteByte(c)
prev = c
}
}
return key.String()
}

func main() {
names := []string{
"Bishop", "Carlson", "Carr", "Chapman",
"Franklin", "Greene", "Harper", "Jacobs", "Larson", "Lawrence",
"Lawson", "Louis, XVI", "Lynch", "Mackenzie", "Matthews", "May jnr",
"McCormack", "McDaniel", "McDonald", "Mclaughlin", "Morrison",
"O'Banion", "O'Brien", "Richards", "Silva", "Watkins", "Xi",
"Wheeler", "Willis", "brown, sr", "browne, III", "browne, IV",
"knight", "mitchell", "o'daniel", "bevan", "evans", "D'Souza",
"Hoyle-Johnson", "Vaughan Williams", "de Sousa", "de la Mare II",
}
for _, name := range names {
name2 := nysiis(name)
if len(name2) > 6 {
name2 = fmt.Sprintf("%s(%s)", name2[:6], name2[6:])
}
fmt.Printf("%-16s : %s\n", name, name2)
}
}</syntaxhighlight>

{{out}}
<pre style="height:15em;">
Bishop : BASAP
Carlson : CARLSA(N)
Carr : CAR
Chapman : CAPNAN
Franklin : FRANCL(AN)
Greene : GRAN
Harper : HARPAR
Jacobs : JACAB
Larson : LARSAN
Lawrence : LARANC
Lawson : LASAN
Louis, XVI : L
Lynch : LYNC
Mackenzie : MCANSY
Matthews : MAT
May jnr : MY
McCormack : MCARNA(C)
McDaniel : MCDANA(L)
McDonald : MCDANA(LD)
Mclaughlin : MCLAGL(AN)
Morrison : MARASA(N)
O'Banion : OBANAN
O'Brien : OBRAN
Richards : RACARD
Silva : SALV
Watkins : WATCAN
Xi : X
Wheeler : WALAR
Willis : WAL
brown, sr : BRAN
browne, III : BRAN
browne, IV : BRAN
knight : NAGT
mitchell : MATCAL
o'daniel : ODANAL
bevan : BAFAN
evans : EVAN
D'Souza : DSAS
Hoyle-Johnson : HAYLAJ(ANSAN)
Vaughan Williams : VAGANW(ALAN)
de Sousa : DASAS
de la Mare II : DALANA(R)
</pre>

=={{header|Java}}==
===Hand written codec===
{{works with|Java|8}}
<syntaxhighlight lang="java">import static java.util.Arrays.*;
import static java.util.Arrays.*;
import static java.lang.System.out;

public class NYSIIS {

final static String[][] first = {{"MAC", "MCC"}, {"KN", "N"}, {"K", "C"},
{"PH", "FF"}, {"PF", "FF"}, {"SCH", "SSS"}};

final static String[][] last = {{"EE", "Y"}, {"IE", "Y"}, {"DT", "D"},
{"RT", "D"}, {"RD", "D"}, {"NT", "D"}, {"ND", "D"}};

final static String Vowels = "AEIOU";

public static void main(String[] args) {
stream(args).parallel().map(n -> transcode(n)).forEach(out::println);
}

static String transcode(String s) {
int len = s.length();
StringBuilder sb = new StringBuilder(len);

for (int i = 0; i < len; i++) {
char c = s.charAt(i);
if (c >= 'a' && c <= 'z')
sb.append((char) (c - 32));
else if (c >= 'A' && c <= 'Z')
sb.append(c);
}

replace(sb, 0, first);
replace(sb, sb.length() - 2, last);

len = sb.length();
sb.append(" ");
for (int i = 1; i < len; i++) {
char prev = sb.charAt(i - 1);
char curr = sb.charAt(i);
char next = sb.charAt(i + 1);

if (curr == 'E' && next == 'V')
sb.replace(i, i + 2, "AF");

else if (isVowel(curr))
sb.setCharAt(i, 'A');

else if (curr == 'Q')
sb.setCharAt(i, 'G');

else if (curr == 'Z')
sb.setCharAt(i, 'S');

else if (curr == 'M')
sb.setCharAt(i, 'N');

else if (curr == 'K' && next == 'N')
sb.setCharAt(i, 'N');

else if (curr == 'K')
sb.setCharAt(i, 'C');

else if (sb.indexOf("SCH", i) == i)
sb.replace(i, i + 3, "SSS");

else if (curr == 'P' && next == 'H')
sb.replace(i, i + 2, "FF");

else if (curr == 'H' && (!isVowel(prev) || !isVowel(next)))
sb.setCharAt(i, prev);

else if (curr == 'W' && isVowel(prev))
sb.setCharAt(i, prev);

if (sb.charAt(i) == prev) {
sb.deleteCharAt(i--);
len--;
}
}
sb.setLength(sb.length() - 1); // We've added a space

int lastPos = sb.length() - 1;
if (lastPos > 1) {

if (sb.charAt(lastPos) == 'S') {
sb.setLength(lastPos);
lastPos --;
}
if (sb.lastIndexOf("AY") == lastPos - 1) {
sb.delete(lastPos - 1, lastPos + 1).append("Y");
lastPos --;
}
if (sb.charAt(lastPos) == 'A') {
sb.setLength(lastPos);
lastPos --;
}
}

if (sb.length() > 6)
sb.insert(6, '[').append(']');

return String.format("%s -> %s", s, sb);
}

private static void replace(StringBuilder sb, int start, String[][] maps) {
if (start >= 0)
for (String[] map : maps) {
if (sb.indexOf(map[0]) == start) {
sb.replace(start, start + map[0].length(), map[1]);
break;
}
}
}

private static boolean isVowel(char c) {
return Vowels.indexOf(c) != -1;
}
}
</syntaxhighlight>

<pre style="height:15em;">
Bishop -> BASAP
browneIII -> BRAN
browneIV -> BRANAV
brownsr -> BRANSR
Carlson -> CARLSA[N]
Carr -> CAR
Chapman -> CAPNAN
Franklin -> FRANCL[AN]
Greene -> GRAN
Harper -> HARPAR
Hayes -> HAY
Jacobs -> JACAB
knight -> NAGT
Larson -> LARSAN
Lawrence -> LARANC
Lawson -> LASAN
LouisXVI -> LASXV
Lynch -> LYNC
Mackenzie -> MCANSY
Matthews -> MAT
McCormack -> MCARNA[C]
McDaniel -> MCDANA[L]
McDonald -> MCDANA[LD]
Mclaughlin -> MCLAGL[AN]
mitchell -> MATCAL
Morrison -> MARASA[N]
O'Banion -> OBANAN
O'Brien -> OBRAN
o'daniel -> ODANAL
Richards -> RACARD
Silva -> SALV
Watkins -> WATCAN
Wheeler -> WALAR
Willis -> WAL
</pre>

===Using a library===
This uses the [https://commons.apache.org/proper/commons-codec/apidocs/org/apache/commons/codec/language/Nysiis.html Apache Commons NYSIIS codec].

<syntaxhighlight lang="java">
import org.apache.commons.codec.language.Nysiis;
public class nysiisUsingApache
{
/** NYSIIS codec that wiil allow strings longer than 6 characters
* to be generated
*/
private static Nysiis codec = new Nysiis( false );

/** Encodes name using the codec and checks the result is encoded.
*/
private static void test( String name, String encoded )
{
String result = (String) codec.encode( name );
System.out.print( String.format( "%16s", name ) + " -> " + result );
if( result.compareTo( encoded ) != 0 )
{
System.out.print( " -- expected \"" + encoded + "\"" );
} // if result.compareTo( encoded ) != 0
System.out.println();
} // test

public static void main( String[] args )
{
test( "Bishop", "BASAP" );
test( "Carlson", "CARLSAN" );
test( "Carr", "CAR" );
test( "Chapman", "CAPNAN" );
test( "Franklin", "FRANCLAN" );
test( "Greene", "GRAN" );
test( "Harper", "HARPAR" );
test( "Jacobs", "JACAB" );
test( "Larson", "LARSAN" );
test( "Lawrence", "LARANC" );
test( "Lawson", "LASAN" );
test( "Louis, XVI", "LASXV" );
test( "Lynch", "LYNC" );
test( "Mackenzie", "MCANSY" );
test( "Matthews", "MAT" );
test( "McCormack", "MCARNAC" );
test( "McDaniel", "MCDANAL" );
test( "McDonald", "MCDANALD" );
test( "Mclaughlin", "MCLAGLAN" );
test( "Morrison", "MARASAN" );
test( "O'Banion", "OBANAN" );
test( "O'Brien", "OBRAN" );
test( "Richards", "RACARD" );
test( "Silva", "SALV" );
test( "Watkins", "WATCAN" );
test( "Wheeler", "WALAR" );
test( "Willis", "WAL" );
test( "brown, sr", "BRANSR" );
test( "browne, III", "BRAN" );
test( "browne, IV", "BRANAV" );
test( "hayes", "HAY" );
test( "knight", "NAGT" );
test( "mitchell", "MATCAL" );
test( "o'daniel", "ODANAL" );
} // main
} // nysiisUsingApache
</syntaxhighlight>

{{out}}
<pre style="height:15em;">
Bishop -> BASAP
Carlson -> CARLSAN
Carr -> CAR
Chapman -> CAPNAN
Franklin -> FRANCLAN
Greene -> GRAN
Harper -> HARPAR
Jacobs -> JACAB
Larson -> LARSAN
Lawrence -> LARANC
Lawson -> LASAN
Louis, XVI -> LASXV
Lynch -> LYNC
Mackenzie -> MCANSY
Matthews -> MAT
McCormack -> MCARNAC
McDaniel -> MCDANAL
McDonald -> MCDANALD
Mclaughlin -> MCLAGLAN
Morrison -> MARASAN
O'Banion -> OBANAN
O'Brien -> OBRAN
Richards -> RACARD
Silva -> SALV
Watkins -> WATCAN
Wheeler -> WALAR
Willis -> WAL
brown, sr -> BRANSR
browne, III -> BRAN
browne, IV -> BRANAV
hayes -> HAY
knight -> NAGT
mitchell -> MATCAL
o'daniel -> ODANAL
</pre>

=={{header|jq}}==
{{incorrect|jq|Mathews should encode to MAT - see the discussion page}}

'''Adapted from [[#Wren|Wren]]'''

'''Works with jq, the C implementation of jq'''

'''Works with gojq, the Go implementation of jq'''

'''Works with jaq, the Rust implementation of jq'''
<syntaxhighlight lang="jq">
### Generic utilities

def lpad($len): tostring | ($len - length) as $l | (" " * $l) + .;

# Output: a stream
def toChars: explode[] | [.] | implode;

### NYSIIS encoding

def fStrs: [["MAC", "MCC"], ["KN", "N"], ["K", "C"], ["PH", "FF"], ["PF", "FF"], ["SCH", "SSS"]];
def lStrs: [["EE", "Y"], ["IE", "Y"], ["DT", "D"], ["RT", "D"], ["RD", "D"], ["NT", "D"], ["ND", "D"]];
def mStrs: [["EV", "AF"], ["KN", "N"], ["SCH", "SSS"], ["PH", "FF"]];
def eStrs: ["JR", "JNR", "SR", "SNR"];

def isVowel: {"A": true, "E": true, "I": true, "O": true, "U": true}[.];

def isRoman:
{"I": true, "V": true, "X": true} as $roman
| all(explode[] | [.] | implode; $roman[.] );

def splitter: "[ |,]";

def nysiis:
if . == "" then .
else {w: ascii_upcase}
| [.w | splits(splitter)] as $ww
| if ($ww|length) > 1 and ($ww[-1]|isRoman) then .w = .w[0: (.w|length) - ($ww[-1]|length)] end
| .w |= gsub("[ ,'-]"; "")
| reduce eStrs[] as $eStr (.;
if (.w|endswith($eStr)) then .w |= .[0: length - ($eStr|length)] end)
| reduce fStrs[] as $fStr (.;
if (.w|startswith($fStr[0])) then .w |= $fStr[1] + .[$fStr[0] | length :] end )
| reduce lStrs[] as $lStr (.;
if (.w|endswith($lStr[0])) then .w |= .[0:-2] + $lStr[1] end)
| .key = .w[0:1]
| .w |= .[1:]
| reduce mStrs[] as $mStr (.; .w |= gsub($mStr[0]; $mStr[1]))
| .sb = [.key[0:1], (.w|toChars)]
| (.sb|length) as $len
| reduce range(0; $len) as $i (.;
.sb[$i] as $s
| if $s | test("[EIOU]") then .sb[$i] = "A"
elif $s == "Q" then .sb[$i] = "G"
elif $s == "Z" then .sb[$i] = "S"
elif $s == "M" then .sb[$i] = "N"
elif $s == "K" then .sb[$i] = "C"
elif $s == "H"
then
if (.sb[$i-1] | isVowel | not) or ($i < $len-1 and ((.sb[$i+1] | isVowel) | not))
then .sb[$i] = .sb[$i-1]
end
elif $s == "W"
then if (.sb[$i-1]|isVowel) then .sb[$i] = "A" end
end )
| if .sb[-1] == "S" then .sb |= .[0: -1] end
| if (.sb|length) > 1 and (.sb|join("")|.[-2:] == "AY")
then .sb |= .[0:-2] + ["Y"]
end
| if .sb[-1] == "A" then .sb |= .[0:-1] end
| .prev = .key[0:1]
| reduce range(1; .sb|length) as $j (.;
.sb[$j] as $c
| if (.prev != $c)
then .key += $c
| .prev = $c
end )
| .key
end ;

def names: [
"Bishop", "Carlson", "Carr", "Chapman",
"Franklin", "Greene", "Harper", "Jacobs", "Larson", "Lawrence",
"Lawson", "Louis, XVI", "Lynch", "Mackenzie", "Matthews", "May jnr",
"McCormack", "McDaniel", "McDonald", "Mclaughlin", "Morrison",
"O'Banion", "O'Brien", "Richards", "Silva", "Watkins", "Xi",
"Wheeler", "Willis", "brown, sr", "browne, III", "browne, IV",
"knight", "mitchell", "o'daniel", "bevan", "evans", "D'Souza",
"Hoyle-Johnson", "Vaughan Williams", "de Sousa", "de la Mare II"
];

names[]
| . as $name
| nysiis
| . as $ny
| (if length > 6 then "\($ny[0:6])(\($ny[6:]))"
else $ny
end) as $name2
| "\($name|lpad(16)) : \($name2)"
</syntaxhighlight>
{{output}}
<pre style="height:15em;">
Bishop : BASAP
Carlson : CARLSA(N)
Carr : CAR
Chapman : CAPNAN
Franklin : FRANCL(AN)
Greene : GRAN
Harper : HARPAR
Jacobs : JACAB
Larson : LARSAN
Lawrence : LARANC
Lawson : LASAN
Louis, XVI : LA
Lynch : LYNC
Mackenzie : MCANSY
Matthews : MATA
May jnr : MY
McCormack : MCARNA(C)
McDaniel : MCDANA(L)
McDonald : MCDANA(LD)
Mclaughlin : MCLAGL(AN)
Morrison : MARASA(N)
O'Banion : OBANAN
O'Brien : OBRAN
Richards : RACARD
Silva : SALV
Watkins : WATCAN
Xi : X
Wheeler : WALAR
Willis : WAL
brown, sr : BRAN
browne, III : BRAN
browne, IV : BRAN
knight : NAGT
mitchell : MATCAL
o'daniel : ODANAL
bevan : BAFAN
evans : EVAN
D'Souza : DSAS
Hoyle-Johnson : HAYLAJ(ANSAN)
Vaughan Williams : VAGANW(ALAN)
de Sousa : DASAS
de la Mare II : DALANA(R)
</pre>

=={{header|Julia}}==

{{works with|Julia|1.0+}}
{{trans|Python}}

<syntaxhighlight lang="julia">using Printf

function replaceat(text::AbstractString, position::Int, fromlist, tolist)
for (f, t) in zip(fromlist, tolist)
if startswith(text[position:end], f)
return text[1:position-1] * t * text[position+length(f):end]
end
end
return text
end

function replaceend(text::AbstractString, fromlist, tolist)
for (f, t) in zip(fromlist, tolist)
if endswith(text, f)
return text[1:end-length(f)] * t
end
end
return text
end

function nysiis(name::AbstractString)
vowels = ["A", "E", "I", "O", "U"]

name = uppercase(filter(isalpha, name))
name = replaceat(name, 1, ["MAC", "KN", "K", "PH", "PF", "SCH"],
["MCC", "N", "C", "FF", "FF", "SSS"])
name = replaceend(name, ["EE", "IE", "DT", "RT", "RD", "NT", "ND"],
["Y", "Y", "D", "D", "D", "D", "D"])
key, key1 = name[1:1], ""
for i in 2:length(name)
prev, curr = name[(i:i).-1], name[i:i]
next = i < length(name) ? name[(i:i).+1] : ""
name = replaceat(name, i, vcat("EV", vowels), ["AF", "A", "A", "A", "A", "A"])
name = replaceat(name, i, "QZM", "GSN")
name = replaceat(name, i, ["KN", "K"], ["N", "C"])
name = replaceat(name, i, ["SCH", "PH"], ["SSS", "FF"])
if curr == "H" && (prev ∉ vowels || next ∉ vowels)
name = name[1:i-1] * prev * name[i+1:end]
end
if curr == "W" && prev ∈ vowels
name = name[1:i-1] * "A" * name[i+1:end]
end
if !isempty(key) && key[end:end] != name[i:i]
key *= name[i:i]
end
i += 1
end
key = replaceend(key, ["S"], [""])
key = replaceend(key, ["AY"], ["Y"])
key = replaceend(key, ["A"], [""])
return key1 * key
end

for name in ["Bishop", "Carlson", "Carr", "Chapman", "Franklin",
"Greene", "Harper", "Jacobs", "Larson", "Lawrence",
"Lawson", "Louis, XVI", "Lynch", "Mackenzie", "Matthews",
"McCormack", "McDaniel", "McDonald", "Mclaughlin", "Morrison",
"O'Banion", "O'Brien", "Richards", "Silva", "Watkins",
"Wheeler", "Willis", "brown, sr", "browne, III", "browne, IV",
"knight", "mitchell", "o'daniel"]
@printf("%15s: %s\n", name, nysiis(name))
end
</syntaxhighlight>

{{out}}
<pre style="height:15em;">
Bishop: BASAP
Carlson: CARLSAN
Carr: CAR
Chapman: CAPNAN
Franklin: FRANCLAN
Greene: GRAN
Harper: HARPAR
Jacobs: JACAB
Larson: LARSAN
Lawrence: LARANC
Lawson: LASAN
Louis, XVI: LASXV
Lynch: LYNC
Mackenzie: MCANSY
Matthews: MAT
McCormack: MCARNAC
McDaniel: MCDANAL
McDonald: MCDANALD
Mclaughlin: MCLAGLAN
Morrison: MARASAN
O'Banion: OBANAN
O'Brien: OBRAN
Richards: RACARD
Silva: SALV
Watkins: WATCAN
Wheeler: WALAR
Willis: WAL
brown, sr: BRANSR
browne, III: BRAN
browne, IV: BRANAV
knight: NAGT
mitchell: MATCAL
o'daniel: ODANAL
</pre>

=={{header|Kotlin}}==

<syntaxhighlight lang="scala">// version 1.1.3

val fStrs = listOf("MAC" to "MCC", "KN" to "N", "K" to "C", "PH" to "FF",
"PF" to "FF", "SCH" to "SSS")

val lStrs = listOf("EE" to "Y", "IE" to "Y", "DT" to "D", "RT" to "D",
"RD" to "D", "NT" to "D", "ND" to "D")

val mStrs = listOf("EV" to "AF", "KN" to "N", "SCH" to "SSS", "PH" to "FF")

val eStrs = listOf("JR", "JNR", "SR", "SNR")

fun Char.isVowel() = this in "AEIOU"

fun String.isRoman() = this.all { it in "IVX" }
fun nysiis(word: String): String {
if (word.isEmpty()) return word
var w = word.toUpperCase()
val ww = w.split(' ', ',')
if (ww.size > 1 && ww.last().isRoman()) w = w.dropLast(ww.last().length)
for (c in " ,'-") w = w.replace(c.toString(), "")
for (eStr in eStrs)
if (w.endsWith(eStr)) w = w.dropLast(eStr.length)

for (fStr in fStrs)
if (w.startsWith(fStr.first)) w = w.replaceFirst(fStr.first, fStr.second)
for (lStr in lStrs)
if (w.endsWith(lStr.first)) w = w.dropLast(2) + lStr.second
val key = StringBuilder().append(w[0])
w = w.drop(1)
for (mStr in mStrs) w = w.replace(mStr.first, mStr.second)
val sb = StringBuilder().append(key[0]).append(w)
var i = 1
var len = sb.length
while (i < len) {
when (sb[i]) {
in "EIOU" -> sb[i] = 'A'
'Q' -> sb[i] = 'G'
'Z' -> sb[i] = 'S'
'M' -> sb[i] = 'N'
'K' -> sb[i] = 'C'
'H' -> if (!sb[i - 1].isVowel() || (i < len - 1 && !sb[i + 1].isVowel())) sb[i] = sb[i - 1]
'W' -> if (sb[i - 1].isVowel()) sb[i] = 'A'
}
if (sb[i] != sb[i - 1]) {
i++
}
else {
sb.deleteCharAt(i) // deprecated method - should use deleteAt in later Kotlin versions
len--
}
}
if (sb[len - 1] == 'S') {
sb.setLength(len - 1)
len--
}
if (len > 1 && sb.substring(len - 2) == "AY") {
sb.setLength(len - 2)
sb.append("Y")
len--
}
if (len > 0 && sb[len - 1] == 'A') {
sb.setLength(len - 1)
len--
}
var prev = key[0]
for (j in 1 until len) {
val c = sb[j]
if (prev != c) {
key.append(c)
prev = c
}
}
return key.toString()
}

fun main(args:Array<String>) {
val names = listOf(
"Bishop", "Carlson", "Carr", "Chapman",
"Franklin", "Greene", "Harper", "Jacobs", "Larson", "Lawrence",
"Lawson", "Louis, XVI", "Lynch", "Mackenzie", "Matthews", "May jnr",
"McCormack", "McDaniel", "McDonald", "Mclaughlin", "Morrison",
"O'Banion", "O'Brien", "Richards", "Silva", "Watkins", "Xi",
"Wheeler", "Willis", "brown, sr", "browne, III", "browne, IV",
"knight", "mitchell", "o'daniel", "bevan", "evans", "D'Souza",
"Hoyle-Johnson", "Vaughan Williams", "de Sousa", "de la Mare II"
)
for (name in names) {
var name2 = nysiis(name)
if (name2.length > 6) name2 = "${name2.take(6)}(${name2.drop(6)})"
println("${name.padEnd(16)} : $name2")
}
}</syntaxhighlight>

{{out}}
<pre style="height:15em;>
Bishop : BASAP
Carlson : CARLSA(N)
Carr : CAR
Chapman : CAPNAN
Franklin : FRANCL(AN)
Greene : GRAN
Harper : HARPAR
Jacobs : JACAB
Larson : LARSAN
Lawrence : LARANC
Lawson : LASAN
Louis, XVI : L
Lynch : LYNC
Mackenzie : MCANSY
Matthews : MAT
May jnr : MY
McCormack : MCARNA(C)
McDaniel : MCDANA(L)
McDonald : MCDANA(LD)
Mclaughlin : MCLAGL(AN)
Morrison : MARASA(N)
O'Banion : OBANAN
O'Brien : OBRAN
Richards : RACARD
Silva : SALV
Watkins : WATCAN
Xi : X
Wheeler : WALAR
Willis : WAL
brown, sr : BRAN
browne, III : BRAN
browne, IV : BRAN
knight : NAGT
mitchell : MATCAL
o'daniel : ODANAL
bevan : BAFAN
evans : EVAN
D'Souza : DSAS
Hoyle-Johnson : HAYLAJ(ANSAN)
Vaughan Williams : VAGANW(ALAN)
de Sousa : DASAS
de la Mare II : DALANA(R)
</pre>

=={{header|Nim}}==
{{trans|Kotlin}}
<syntaxhighlight lang="nim">import strutils

const
FStrs = [("MAC", "MCC"), ("KN", "N"), ("K", "C"),
("PH", "FF"), ("PF", "FF"), ("SCH", "SSS")]
LStrs = [("EE", "Y"), ("IE", "Y"), ("DT", "D"),
("RT", "D"), ("RD", "D"), ("NT", "D"), ("ND", "D")]
MStrs = [("EV", "AF"), ("KN", "N"), ("SCH", "SSS"), ("PH", "FF")]
EStrs = ["JR", "JNR", "SR", "SNR"]


func isVowel(c: char): bool = c in {'A', 'E', 'I', 'O', 'U'}

func isRoman(s: string): bool = s.allCharsInSet({'I', 'V', 'X'})


func nysiis(word: string): string =

if word.len == 0: return
var word = word.toUpperAscii()
let fields = word.split({' ', ','})
if fields.len > 1:
let last = fields[^1]
if last.isRoman: word.setLen(word.len - last.len)
word = word.multiReplace((" ", ""), (",", ""), ("'", ""), ("-", ""))
for eStr in EStrs:
if word.endsWith(eStr): word.setLen(word.len - eStr.len)
for fStr in FStrs:
if word.startsWith(fStr[0]): word[0..fStr[0].high] = fStr[1]
for lStr in LStrs:
if word.endsWith(lStr[0]): word[^2..^1] = lStr[1]

result.add word[0]
word.delete(0..0)
for mStr in MStrs:
word = word.replace(mStr[0], mStr[1])
var s = result[0] & word
var len = s.len
for i in 1..<len:
case s[i]
of 'E', 'I', 'O', 'U': s[i] = 'A'
of 'Q': s[i] = 'G'
of 'Z': s[i] = 'S'
of 'M': s[i] = 'N'
of 'K': s[i] = 'C'
of 'H': (if not s[i-1].isVowel or i < len - 1 and not s[i+1].isVowel: s[i] = s[i-1])
of 'W': (if s[i-1].isVowel: s[i] = 'A')
else: discard

if s[len-1] == 'S':
s.setLen(len-1)
dec len
if len > 1 and s[len-2..len-1] == "AY":
s.delete(len-2..len-2)
dec len
if len > 0 and s[len-1] == 'A':
s.setLen(len-1)
dec len
if len > 0 and s[len-1] == 'A':
s.setLen(len-1)
dec len

var prev = result[0]
for i in 1..<len:
let c = s[i]
if prev != c:
result.add c
prev = c


const Names = ["Bishop", "Carlson", "Carr", "Chapman",
"Franklin", "Greene", "Harper", "Jacobs", "Larson", "Lawrence",
"Lawson", "Louis, XVI", "Lynch", "Mackenzie", "Matthews", "May jnr",
"McCormack", "McDaniel", "McDonald", "Mclaughlin", "Morrison",
"O'Banion", "O'Brien", "Richards", "Silva", "Watkins", "Xi",
"Wheeler", "Willis", "brown, sr", "browne, III", "browne, IV",
"knight", "mitchell", "o'daniel", "bevan", "evans", "D'Souza",
"Hoyle-Johnson", "Vaughan Williams", "de Sousa", "de la Mare II"]

for name1 in Names:
var name2 = nysiis(name1)
if name2.len > 6:
name2 = "$1($2)".format(name2[0..5], name2[6..^1])
echo name1.alignLeft(16), ": ", name2</syntaxhighlight>

{{out}}
<pre>Bishop : BASAP
Carlson : CARLSA(N)
Carr : CAR
Chapman : CAPNAN
Franklin : FRANCL(AN)
Greene : GRAN
Harper : HARPAR
Jacobs : JACAB
Larson : LARSAN
Lawrence : LARANC
Lawson : LASAN
Louis, XVI : LA
Lynch : LYNC
Mackenzie : MCANSY
Matthews : MAT
May jnr : MY
McCormack : MCARNA(C)
McDaniel : MCDANA(L)
McDonald : MCDANA(LD)
Mclaughlin : MCLAGL(AN)
Morrison : MARASA(N)
O'Banion : OBANAN
O'Brien : OBRAN
Richards : RACARD
Silva : SALV
Watkins : WATCAN
Xi : X
Wheeler : WALAR
Willis : WAL
brown, sr : BRAN
browne, III : BRAN
browne, IV : BRAN
knight : NAGT
mitchell : MATCAL
o'daniel : ODANAL
bevan : BAFAN
evans : EVAN
D'Souza : DSAS
Hoyle-Johnson : HAYLAJ(ANSAN)
Vaughan Williams: VAGANW(ALAN)
de Sousa : DASAS
de la Mare II : DALANA(R)</pre>

=={{header|Perl}}==
{{trans|Raku}}
<syntaxhighlight lang="perl">sub no_suffix {
my($name) = @_;
$name =~ s/\h([JS]R)|([IVX]+)$//i;
return uc $name;
}

sub nysiis {
my($name) = @_;
local($_) = uc $name;

s/[^A-Z]//g;
s/^MAC/MCC/;
s/^P[FH]/FF/;
s/^SCH/SSS/;
s/^KN/N/;
s/[IE]E$/Y/;
s/[DRN]T$/D/;
s/[RN]D$/D/;
s/(.)EV/$1AF/g;
s/(.)[AEIOU]+/$1A/g;
s/(.)Q/$1G/g;
s/(.)Z/$1S/g;
s/(.)M/$1N/g;
s/(.)KN/$1N/g;
s/(.)K/$1C/g;
s/(.)SCH/$1S/g;
s/(.)PF/$1F/g;
s/(.)K/$1C/g;
s/(.)H([^AEIOU])/$1$2/g;
s/([^AEIOU])H/$1/g;
s/(.)W/$1/g;
s/AY$/Y/;
s/S$//;
s/A$//;
s/(.)\1+/$1/g;
return $_;
}

for (
"knight", "mitchell", "o'daniel", "brown sr", "browne III",
"browne IV", "O'Banion", "Mclaughlin", "McCormack", "Chapman",
"Silva", "McDonald", "Lawson", "Jacobs", "Greene",
"O'Brien", "Morrison", "Larson", "Willis", "Mackenzie",
"Carr", "Lawrence", "Matthews", "Richards", "Bishop",
"Franklin", "McDaniel", "Harper", "Lynch", "Watkins",
"Carlson", "Wheeler", "Louis XVI"
) {
my $nysiis = nysiis no_suffix $_;
$nysiis =~ s/^(......)(.*)$/$1\[$2\]/ if length($nysiis) > 6;
printf "%10s, %s\n", $_, $nysiis;
}
</syntaxhighlight>
{{out}}
<pre style="height:35ex"> knight, NAGT
mitchell, MATCAL
o'daniel, ODANAL
brown sr, BRAN
browne III, BRAN
browne IV, BRAN
O'Banion, OBANAN
Mclaughlin, MCLAGL[AN]
McCormack, MCARNA[C]
Chapman, CAPNAN
Silva, SALV
McDonald, MCDANA[LD]
Lawson, LASAN
Jacobs, JACAB
Greene, GRAN
O'Brien, OBRAN
Morrison, MARASA[N]
Larson, LARSAN
Willis, WAL
Mackenzie, MCANSY
Carr, CAR
Lawrence, LARANC
Matthews, MAT
Richards, RACARD
Bishop, BASAP
Franklin, FRANCL[AN]
McDaniel, MCDANA[L]
Harper, HARPAR
Lynch, LYNC
Watkins, WATCAN
Carlson, CARLSA[N]
Wheeler, WALAR
Louis XVI, L</pre>

=={{header|Phix}}==
{{trans|Go}}
<!--(phixonline)-->
<syntaxhighlight lang="phix">
with javascript_semantics
function isVowel(integer byte)
return find(byte,"AEIOU")!=0
end function
function nysiis(string word)
if word == "" then return "" end if
word = upper(word)
sequence ww = split_any(word, ", ")
if length(ww)>1 then
if length(filter(ww[$],"out","IVX"))=0 then
word = word[1..-length(ww[$])-1]
end if
end if
word = substitute_all(word, " ,'-", repeat("",4))
for e in {"JR", "JNR", "SR", "SNR"} do
if ends(e,word) then word=word[1..-length(e)-1] end if
end for
for f in {{"MAC","MCC"}, {"KN","N"}, {"K","C"},
{"PH","FF"}, {"PF","FF"}, {"SCH","SSS"}} do
if begins(f[1],word) then
word[1..length(f[1])] = f[2]
end if
end for
integer e = length(word)>=2 and find(word[-2..-1],
{"EE","IE","DT","RT","RD","NT","ND"})
if e then
word[-2..-1] = iff(e<=2?"Y":"D")
end if
integer initial = word[1]
string key = word[1..1]
word = word[2..$]
word = substitute_all(word,{"EV","KN","SCH","PH"},
{"AF","N", "SSS","FF"})
string sb = key&word
integer le := length(sb)
for i=2 to le do
switch sb[i] do
case 'E', 'I', 'O', 'U': sb[i] = 'A'
case 'Q': sb[i] = 'G'
case 'Z': sb[i] = 'S'
case 'M': sb[i] = 'N'
case 'K': sb[i] = 'C'
case 'H': if (i> 1 and not isVowel(sb[i-1]))
or (i<le and not isVowel(sb[i+1])) then
sb[i] = sb[i-1]
end if
case 'W': if isVowel(sb[i-1]) then
sb[i] = sb[i-1]
end if
end switch
end for
integer prev := initial
for j=2 to le do
integer c := sb[j]
if prev != c then
key &= c
prev = c
end if
end for
if length(key)>=1 and key[$] == 'S' then key[$ ..$] = "" end if
if length(key)>=2 and key[-2..-1] == "AY" then key[$-1..$] = "Y" end if
if length(key)>=1 and key[$] == 'A' then key[$ ..$] = "" end if
return key
end function
constant tests = {
{ "Bishop", "BASAP" },
{ "Carlson", "CARLSAN" },
{ "Carr", "CAR" },
{ "Chapman", "CAPNAN" },
{ "Franklin", "FRANCLAN" },
{ "Greene", "GRAN" },
{ "Harper", "HARPAR" },
{ "Jacobs", "JACAB" },
{ "Larson", "LARSAN" },
{ "Lawrence", "LARANC" },
{ "Lawson", "LASAN" },
{ "Louis, XVI", "L" }, -- (see note)
{ "Lynch", "LYNC" },
{ "Mackenzie", "MCANSY" },
{ "Matthews", "MAT" }, -- (see note)
{ "May jnr", "MY" },
{ "McCormack", "MCARNAC" },
{ "McDaniel", "MCDANAL" },
{ "McDonald", "MCDANALD" },
{ "Mclaughlin", "MCLAGLAN" },
{ "Morrison", "MARASAN" },
{ "O'Banion", "OBANAN" },
{ "O'Brien", "OBRAN" },
{ "Richards", "RACARD" },
{ "Silva", "SALV" },
{ "Watkins", "WATCAN" },
{ "Wheeler", "WALAR" },
{ "Willis", "WAL" }, -- (see note)
{ "Xi", "X" },
{ "bevan", "BAFAN" },
{ "brown, sr", "BRAN" },
{ "brown sr", "BRAN" },
{ "browne, III", "BRAN" },
{ "browne, IV", "BRAN" },
{ "evans", "EVAN" },
{ "knight", "NAGT" },
{ "mitchell", "MATCAL" },
{ "o'daniel", "ODANAL" },
{ "D'Souza", "DSAS" },
{ "de Sousa", "DASAS" },
{ "Hoyle-Johnson", "HAYLAJANSAN" },
{ "Vaughan Williams", "VAGANWALAN" },
{ "de la Mare II", "DALANAR" } }
integer errors = 0
for i=1 to length(tests) do
string {name,expected} = tests[i],
actual := nysiis(name)
if actual!=expected then
errors += 1
if length(actual) > 6 then
actual = sprintf("%s(%s)", {actual[1..6], actual[7..$]})
end if
printf(1,"%-16s : %s\n", {name, actual})
end if
end for
printf(1,"All tests completed, %d errors\n",errors)
</syntaxhighlight>
Note: After some careful consideration, I have decided that all three (see note) tests <i>are</i> in fact correct, or at least follow wikipedia, specifically step 6 <i>before</i> step 7.
{{out}}
<pre>
All tests completed, 0 errors
</pre>

=={{header|Python}}==
A literal translation of the algorithm from the [[wp:New York State Identification and Intelligence System|Wikipedia article]].
<syntaxhighlight lang="python">import re

_vowels = 'AEIOU'

def replace_at(text, position, fromlist, tolist):
for f, t in zip(fromlist, tolist):
if text[position:].startswith(f):
return ''.join([text[:position],
t,
text[position+len(f):]])
return text

def replace_end(text, fromlist, tolist):
for f, t in zip(fromlist, tolist):
if text.endswith(f):
return text[:-len(f)] + t
return text

def nysiis(name):
name = re.sub(r'\W', '', name).upper()
name = replace_at(name, 0, ['MAC', 'KN', 'K', 'PH', 'PF', 'SCH'],
['MCC', 'N', 'C', 'FF', 'FF', 'SSS'])
name = replace_end(name, ['EE', 'IE', 'DT', 'RT', 'RD', 'NT', 'ND'],
['Y', 'Y', 'D', 'D', 'D', 'D', 'D'])
key, key1 = name[0], ''
i = 1
while i < len(name):
#print(i, name, key1, key)
n_1, n = name[i-1], name[i]
n1_ = name[i+1] if i+1 < len(name) else ''
name = replace_at(name, i, ['EV'] + list(_vowels), ['AF'] + ['A']*5)
name = replace_at(name, i, 'QZM', 'GSN')
name = replace_at(name, i, ['KN', 'K'], ['N', 'C'])
name = replace_at(name, i, ['SCH', 'PH'], ['SSS', 'FF'])
if n == 'H' and (n_1 not in _vowels or n1_ not in _vowels):
name = ''.join([name[:i], n_1, name[i+1:]])
if n == 'W' and n_1 in _vowels:
name = ''.join([name[:i], 'A', name[i+1:]])
if key and key[-1] != name[i]:
key += name[i]
i += 1
key = replace_end(key, ['S'], [''])
key = replace_end(key, ['AY'], ['Y'])
key = replace_end(key, ['A'], [''])
return key1 + key

if __name__ == '__main__':
names = ['Bishop', 'Carlson', 'Carr', 'Chapman', 'Franklin',
'Greene', 'Harper', 'Jacobs', 'Larson', 'Lawrence',
'Lawson', 'Louis, XVI', 'Lynch', 'Mackenzie', 'Matthews',
'McCormack', 'McDaniel', 'McDonald', 'Mclaughlin', 'Morrison',
"O'Banion", "O'Brien", 'Richards', 'Silva', 'Watkins',
'Wheeler', 'Willis', 'brown, sr', 'browne, III', 'browne, IV',
'knight', 'mitchell', "o'daniel"]
for name in names:
print('%15s: %s' % (name, nysiis(name)))</syntaxhighlight>
{{out}}
<pre> Bishop: BASAP
Carlson: CARLSAN
Carr: CAR
Chapman: CAPNAN
Franklin: FRANCLAN
Greene: GRAN
Harper: HARPAR
Jacobs: JACAB
Larson: LARSAN
Lawrence: LARANC
Lawson: LASAN
Louis, XVI: LASXV
Lynch: LYNC
Mackenzie: MCANSY
Matthews: MAT
McCormack: MCARNAC
McDaniel: MCDANAL
McDonald: MCDANALD
Mclaughlin: MCLAGLAN
Morrison: MARASAN
O'Banion: OBANAN
O'Brien: OBRAN
Richards: RACARD
Silva: SALV
Watkins: WATCAN
Wheeler: WALAR
Willis: WAL
brown, sr: BRANSR
browne, III: BRAN
browne, IV: BRANAV
knight: NAGT
mitchell: MATCAL
o'daniel: ODANAL
</pre>

=={{header|Racket}}==
{{incorrect|Racket|Willis should encode to WAL and Mathews to MAT - see the discussion page}}

{{trans|Python}}
This is a translation of [[Python]] to ensure that these results are consistent.
This allows them to be tested against a known output from another solution.

If any of the <code>check-equal?</code>s fails, it will print an error message.
None of them fail, so no output is seen.

I&rsquo;ve gone out of my way to number the rules &mdash; which may make it a little
verbose.

<syntaxhighlight lang="racket">#lang racket/base
(require racket/string racket/match)
(define (str-rplc-at str replacement start (end (string-length str)))
(string-append (substring str 0 start) replacement (substring str end)))

(define (split-on-commas s) (string-split s "," #:trim? #f))

(define (str-rplc-at* fxt pos . more)
(match more
[(list (app split-on-commas froms) (app split-on-commas tos) even-more ...)
(define txt-maybe
(for/first ((f froms) (t tos) #:when (string-prefix? (substring fxt pos) f))
(str-rplc-at fxt t pos (+ pos (string-length f)))))
(apply str-rplc-at* (or txt-maybe fxt) pos even-more)]
[_ fxt]))

(define (str-rplc-end* txf . more)
(match more
[(list (app split-on-commas froms) (app split-on-commas tos) even-more ...)
(define txt-maybe
(for/first ((f froms) (t tos) #:when (string-suffix? txf f))
(str-rplc-at txf t (- (string-length txf) (string-length f)))))
(apply str-rplc-end* (or txt-maybe txf) even-more)]
[_ txf]))

(define vowels '("A" "E" "I" "O" "U"))
(define (vowel? s) (member s vowels))

;; ---------------------------------------------------------------------------------------------------
(define (normalise n) (regexp-replace* #px"\\W" (string-upcase n) ""))

(define (r:1 n) (str-rplc-at* n 0 "MAC,KN,K,PH,PF,SCH" "MCC,N,C,FF,FF,SSS"))

(define (r:2 n) (str-rplc-end* n "EE,IE,DT,RT,RD,NT,ND" "Y,Y,D,D,D,D,D"))

(define (r:3/4 in)
(define (loop-4 name-4.1 key-3 i)
(cond
[(< i (string-length name-4.1))
(define name-4.2/3/4
(str-rplc-at* name-4.1 i "EV,A,E,I,O,U" "AF,A,A,A,A,A" #|4.1|# "Q,Z,M" "G,S,N" #|4.2|#
"KN,K" "N,C" #|4.3|# "SCH,PH" "SSS,FF" #|4.4|#))
(define name-4.5/6
(match-let ([(or (regexp "^(.)(.)(.)" (list n_1 n n1_))
(regexp "^(.)(.)" (list (app (λ _ "") n1_) n_1 n)))
(substring name-4.1 (sub1 i))])
(match n
["H" #:when (or (not (vowel? n_1)) (not (vowel? n1_)))
(str-rplc-at name-4.2/3/4 n_1 i (add1 i))] ; 4.5
["W" #:when (vowel? n_1) (str-rplc-at name-4.2/3/4 "A" i (add1 i))] ; 4.6
[_ name-4.2/3/4])))
(define name-4.6_i (substring name-4.5/6 i (add1 i)))
(define key-4.7 (if (string=? name-4.6_i (substring key-3 (sub1 (string-length key-3))))
key-3 (string-append key-3 name-4.6_i)))
(loop-4 name-4.5/6 key-4.7 (add1 i))]
[else key-3]))
(loop-4 in (substring in 0 1) 1))

(define (r:5/6/7/8 n) (str-rplc-end* n "S,AY,A" ",Y,"))

(define r:9 (match-lambda [(regexp #px"^(.{6})(.+)" (list _ l r)) (format "~a[~a]" l r)] [n n]))

(define nysiis (apply compose (reverse (list normalise r:1 r:2 r:3/4 r:5/6/7/8 r:9))))

(module+ test
(require rackunit)
(define names
(list "Bishop" "Carlson" "Carr" "Chapman" "Franklin" "Greene" "Harper" "Jacobs" "Larson"
"Lawrence" "Lawson" "Louis, XVI" "Lynch" "Mackenzie" "Matthews" "McCormack" "McDaniel"
"McDonald" "Mclaughlin" "Morrison" "O'Banion" "O'Brien" "Richards" "Silva" "Watkins"
"Wheeler" "Willis" "brown, sr" "browne, III" "browne, IV" "knight" "mitchell" "o'daniel"))

(define py-nysiis-names ; results from python (with [] added)
(list "BASAP" "CARLSA[N]" "CAR" "CAPNAN" "FRANCL[AN]" "GRAN" "HARPAR" "JACAB" "LARSAN" "LARANC"
"LASAN" "LASXV" "LYNC" "MCANSY" "MATA" "MCARNA[C]" "MCDANA[L]" "MCDANA[LD]" "MCLAGL[AN]"
"MARASA[N]" "OBANAN" "OBRAN" "RACARD" "SALV" "WATCAN" "WALAR" "WALA" "BRANSR" "BRAN"
"BRANAV" "NAGT" "MATCAL" "ODANAL"))

(for ((n names) (p py-nysiis-names))
(check-equal? (nysiis n) p (format "(nysiis ~s) = ~s" n p))))</syntaxhighlight>


=={{header|Perl 6}}==
=={{header|Raku}}==
(formerly Perl 6)
{{Works with|rakudo|2017.05}}
This implementation removes common name suffixes similar to the reference implementation, even though it is not specified in the task description or on the linked [[wp:New York State Identification and Intelligence System|NYSIIS]] page. This algorithm isn't too friendly to certain French kings. :)
This implementation removes common name suffixes similar to the reference implementation, even though it is not specified in the task description or on the linked [[wp:New York State Identification and Intelligence System|NYSIIS]] page. This algorithm isn't too friendly to certain French kings. :)


<lang perl6>sub no_suffix ($name) {
<syntaxhighlight lang="raku" line>sub no_suffix ($name) {
$name.uc.subst: /\h (<[JS]>R) | (<[IVX]>+) $/, '';
$name.uc.subst: /\h (<[JS]>R) | (<[IVX]>+) $/, '';
}
}
Line 225: Line 2,200:
s:c(1):g/H(<-[AEIOU]>)/$0/;
s:c(1):g/H(<-[AEIOU]>)/$0/;
s:g/(<-[AEIOU]>)H/$0/;
s:g/(<-[AEIOU]>)H/$0/;
s:g/(<-[AEIOU]>)W/$0/;
s:g/(.)W/$0/;
s/ AY$ /Y/;
s/ AY$ /Y/;
s/ S$ //;
s/ S$ //;
Line 231: Line 2,206:
s:g/ (.)$0+ /$0/;
s:g/ (.)$0+ /$0/;
}
}
return $name;
}
}


Line 248: Line 2,224:
}
}
printf "%10s, %s\n", $_, $nysiis;
printf "%10s, %s\n", $_, $nysiis;
}</lang>
}</syntaxhighlight>


Output:
Output:
Line 286: Line 2,262:
Wheeler, WALAR
Wheeler, WALAR
Louis XVI, L
Louis XVI, L
</pre>

=={{header|REXX}}==
This REXX version allows a blank to be inserted into names by using an underscore or underbar character &nbsp; [ <big><big>'''_'''</big></big> ].

Code was added to the REXX program to allow various titles.

Any post-nominal letters (generational, honorific, professional,or other) &nbsp; ending in a period is ignored as well as most Roman numeral letters.

If the rule of only returning (up to) six characters is to be enforced, then the last REXX statement should be
replaced with:
<syntaxhighlight lang="rexx">return strip( left(key, 6) ) /*return the leftmost six characters. */</syntaxhighlight>
<syntaxhighlight lang="rexx">/*REXX program implements the NYSIIS phonetic algorithm (for various test names). */
@@= "Bishop brown_sr browne_III browne_IV Carlson Carr Chapman D'Souza de_Sousa Franklin",
"Greene Harper Hoyle-Johnson Jacobs knight Larson Lawrence Lawson Louis_XVI Lynch",
"Mackenzie Marshall,ESQ Matthews McCormack McDaniel McDonald Mclaughlin mitchell Morrison",
"O'Banion O'Brien o'daniel Richards Silva Vaughan_Williams Watkins Wheeler Willis Xavier,MD."
parse upper arg z; if z='' then z= @@ /*obtain optional name list from the CL*/

do i=1 for words(z) /*process each name (word) in the list.*/
xx= translate( word(z, i), , '_') /*reconstitute any blanks using TRANS. */
say right(xx, 35) ' ──► ' nysiis(xx) /*display some stuff to the terminal. */
end /*i*/
exit 0 /*stick a fork in it, we're all done. */
/*──────────────────────────────────────────────────────────────────────────────────────*/
$: p= substr(x,j-1,1) /*prev*/; n= substr(x,j+1,1) /*next*/; return substr(x,j,arg(1))
vowel: return pos(arg(1), 'AEIOUaeiou') \== 0 /*returns 1 if the argument has a vowel*/
/*──────────────────────────────────────────────────────────────────────────────────────*/
nysiis: procedure; arg x; x= space( translate(x, , ',')) /*elide commas, excess blanks*/
w= words(x); Lw= word(x, w) /*pick off the last word in name list. */
@titles= 'ESQ JNR JR SNR SR' /* [↓] last word post─nominal letters?*/
if w\==1 then if pos('IL', lw)==0 then /*disallow IL as Roman #.*/
if pos(., x)\==0 |, /*Sr. Jr. Esq. ··· ? */
datatype( left(Lw, 1), 'W') |, /*2nd 3rd 4th ··· ? */
verify(Lw, 'IVXL') ==0 |, /*Roman numeral suffix? */
wordpos(x, @titles)\==0 then x= subword(x, 1, w-1)
x= space(x, 0) /*remove all whitespace from the name. */
if left(x, 3)=='MAC' then x= "MCC"substr(x, 4) /*start with MAC ? */
if left(x, 2)=='KN' then x= "N"substr(x, 3) /* " " KN ? */
if left(x, 1)=='K' then x= "C"substr(x, 2) /* " " K ? */
if left(x, 2)=='PH' | left(x,2)=="PF" then x= 'FF'substr(x, 3) /* " " PH,PF?*/
if left(x, 3)=='SCH' then x= "SSS"substr(x, 4) /* " " SCH ? */
r2= right(x, 2)
if wordpos(r2, 'EE IE') \==0 then x= left(x, length(x)-2)"Y" /*ends with ··· ?*/
if wordpos(r2, 'DT RT RD NT ND')\==0 then x= left(x, length(x)-2)"D" /* " " " "*/
key= left(x, 1) /*use first char.*/

do j=2 to length(x); if \datatype($(1), 'U') then iterate /*¬ Latin letter? Skip it*/
if $(2)=='EV' then x= overlay("F", x, j+1) /*have an EV ? Use F */
else x= overlay( translate( $(1), 'AAAAGSN', "EIOUQZM"), x, j)
if $(2)=='KN' then x= left(x, j-1)"N"substr(x, j+1) /*have a KN ? Use N */
else if $(1)=="K" then x= overlay('C',x,j) /* " " K ? Use C */
if $(3)=='SCH' then x= overlay("SSS", x, j) /* " " SCH? Use SSS*/
if $(2)=='PH' then x= overlay("FF", x, j) /* " " PH ? Use FF */
if $(1)=='H' then if \vowel(p) | \vowel(n) then x= overlay( p , x, j)
if $(1)=='W' then if vowel(p) then x= overlay("A", x, j)
if $(1)\== right(key, 1) then key= key || $(1) /*append to KEY.*/
end /*j*/
/* [↓] elide: */
if right(key, 1)=='S' then key= left(key, max(1, length(key) -1)) /*ending S */
if right(key, 2)=='AY' then key= left(key, length(key) -2)"Y" /* " A in AY*/
if right(key, 1)=='A' then key= left(key, max(1, length(key) -1)) /* " A */
return strip(key) /*return the whole key (all of it). */</syntaxhighlight>
{{out|output|text=&nbsp; when using the default input:}}
<pre>
Bishop ──► BASAP
brown sr ──► BRANSR
browne III ──► BRAN
browne IV ──► BRAN
Carlson ──► CARLSAN
Carr ──► CAR
Chapman ──► CAPNAN
D'Souza ──► DSAS
de Sousa ──► DASAS
Franklin ──► FRANCLAN
Greene ──► GRAN
Harper ──► HARPAR
Hoyle-Johnson ──► HAYLAJANSAN
Jacobs ──► JACAB
knight ──► NAGT
Larson ──► LARSAN
Lawrence ──► LARANC
Lawson ──► LASAN
Louis XVI ──► L
Lynch ──► LYNC
Mackenzie ──► MCANSY
Marshall,ESQ ──► MARSALASG
Matthews ──► MAT
McCormack ──► MCARNAC
McDaniel ──► MCDANAL
McDonald ──► MCDANALD
Mclaughlin ──► MCLAGLAN
mitchell ──► MATCAL
Morrison ──► MARASAN
O'Banion ──► OBANAN
O'Brien ──► OBRAN
o'daniel ──► ODANAL
Richards ──► RACARD
Silva ──► SALV
Vaughan Williams ──► VAGANWALAN
Watkins ──► WATCAN
Wheeler ──► WALAR
Willis ──► WAL
Xavier,MD. ──► XAVAR
</pre>

=={{header|Tcl}}==
<syntaxhighlight lang="tcl">proc nysiis {name {truncate false}} {
# Normalize to first word, uppercased, without non-letters
set name [regsub -all {[^A-Z]+} [string toupper [regexp -inline {\S+} $name]] ""]
# Prefix map
foreach {from to} {MAC MCC KN N K C PH FF PF FF SCH SSS} {
if {[regsub ^$from $name $to name]} break
}
# Suffix map
foreach {from to} {EE Y IE Y DT D RT D NT D ND D} {
if {[regsub $from$ $name $to name]} break
}
# Split
regexp (.)(.*) $name -> name rest
# Reduce suffix
regsub -all {[AEIOU]} [regsub -all EV $rest AF] A rest
set rest [string map {Q G Z S M N KN N K C SCH SSS PH FF} $rest]
regsub -all {([^A])H|(.)H(?=[^A])} $rest {\1\2} rest
regsub -all AW $rest A rest
regsub -all {(.)\1+} $rest {\1} rest
regsub {S$} $rest "" rest
regsub {A(Y?)$} $rest {\1} rest
append name $rest
# Apply truncation if needed
if {$truncate} {
set name [string range $name 0 5]
}
return $name
}</syntaxhighlight>
Demonstrating:
<syntaxhighlight lang="tcl">foreach name {
knight mitchell "o'daniel" "brown sr" "browne III"
"browne IV" "O'Banion" Mclaughlin McCormack Chapman
Silva McDonald Lawson Jacobs Greene
"O'Brien" Morrison Larson Willis Mackenzie
Carr Lawrence Matthews Richards Bishop
Franklin McDaniel Harper Lynch Watkins
Carlson Wheeler "Louis XVI"
} {
puts "$name -> [nysiis $name]"
}</syntaxhighlight>
{{out}}
<pre>
knight -> NAGT
mitchell -> MATCAL
o'daniel -> ODANAL
brown sr -> BRAN
browne III -> BRAN
browne IV -> BRAN
O'Banion -> OBANAN
Mclaughlin -> MCLAGLAN
McCormack -> MCARNAC
Chapman -> CHAPNAN
Silva -> SALV
McDonald -> MCDANALD
Lawson -> LASAN
Jacobs -> JACAB
Greene -> GRAN
O'Brien -> OBRAN
Morrison -> MARASAN
Larson -> LARSAN
Willis -> WAL
Mackenzie -> MCANSY
Carr -> CAR
Lawrence -> LARANC
Matthews -> MAT
Richards -> RACARD
Bishop -> BASAP
Franklin -> FRANCLAN
McDaniel -> MCDANAL
Harper -> HARPAR
Lynch -> LYNC
Watkins -> WATCAN
Carlson -> CARLSAN
Wheeler -> WHALAR
Louis XVI -> L
</pre>

=={{header|Wren}}==
{{trans|Kotlin}}
{{libheader|Wren-str}}
{{libheader|Wren-pattern}}
{{libheader|Wren-fmt}}
<syntaxhighlight lang="wren">import "./str" for Str
import "./pattern" for Pattern
import "./fmt" for Fmt

var fStrs = [["MAC", "MCC"], ["KN", "N"], ["K", "C"], ["PH", "FF"], ["PF", "FF"], ["SCH", "SSS"]]
var lStrs = [["EE", "Y"], ["IE", "Y"], ["DT", "D"], ["RT", "D"], ["RD", "D"], ["NT", "D"], ["ND", "D"]]
var mStrs = [["EV", "AF"], ["KN", "N"], ["SCH", "SSS"], ["PH", "FF"]]
var eStrs = ["JR", "JNR", "SR", "SNR"]

var isVowel = Fn.new { |c| "AEIOU".contains(c) }

var isRoman = Fn.new { |s| s.all { |c| "IVX".contains(c) } }

var splitter = Pattern.new("[ |,]")

var nysiis = Fn.new { |word|
if (word == "") return word
var w = Str.upper(word)
var ww = splitter.splitAll(w)
if (ww.count > 1 && isRoman.call(ww[-1])) w = w[0...w.count-ww[-1].count]
for (c in " ,'-") w = w.replace(c, "")
for (eStr in eStrs) {
if (w.endsWith(eStr)) w = w[0...w.count-eStr.count]
}
for (fStr in fStrs) {
if (w.startsWith(fStr[0])) w = fStr[1] + w[fStr[0].count..-1]
}
for (lStr in lStrs) {
if (w.endsWith(lStr[0])) w = w[0..-3] + lStr[1]
}
var key = w[0]
w = w[1..-1]
for (mStr in mStrs) w = w.replace(mStr[0], mStr[1])
var sb = (key[0] + w).toList
var i = 1
var len = sb.count
while (i < len) {
if ("EIOU".contains(sb[i])) {
sb[i] = "A"
} else if (sb[i] == "Q") {
sb[i] = "G"
} else if (sb[i] == "Z") {
sb[i] = "S"
} else if (sb[i] == "M") {
sb[i] = "N"
} else if (sb[i] == "K") {
sb[i] = "C"
} else if (sb[i] == "H") {
if (!isVowel.call(sb[i-1]) || (i < len-1 && !isVowel.call(sb[i+1]))) sb[i] = sb[i-1]
} else if (sb[i] == "W") {
if (isVowel.call(sb[i-1])) sb[i] = "A"
}
if (sb[i] != sb[i-1]) {
i = i + 1
} else {
sb.removeAt(i)
len = len - 1
}
}
if (sb[len-1] == "S") {
sb = sb[0...len-1]
len = len - 1
}
if (len > 1 && Str.sub(sb.join(""), len-2..-1) == "AY") {
sb = sb[0...len-2]
sb = sb + ["Y"]
len = len -1
}
if (len > 0 && sb[len-1] == "A") {
sb = sb[0...len-1]
len = len - 1
}
var prev = key[0]
for (j in 1...len) {
var c = sb[j]
if (prev != c) {
key = key + c
prev = c
}
}
return key
}

var names = [
"Bishop", "Carlson", "Carr", "Chapman",
"Franklin", "Greene", "Harper", "Jacobs", "Larson", "Lawrence",
"Lawson", "Louis, XVI", "Lynch", "Mackenzie", "Matthews", "May jnr",
"McCormack", "McDaniel", "McDonald", "Mclaughlin", "Morrison",
"O'Banion", "O'Brien", "Richards", "Silva", "Watkins", "Xi",
"Wheeler", "Willis", "brown, sr", "browne, III", "browne, IV",
"knight", "mitchell", "o'daniel", "bevan", "evans", "D'Souza",
"Hoyle-Johnson", "Vaughan Williams", "de Sousa", "de la Mare II"
]

for (name in names) {
var name2 = nysiis.call(name)
if (name2.count > 6) name2 = Fmt.swrite("$s($s)", name2[0..5], name2[6..-1])
Fmt.print("$-16s : $s", name, name2)
}</syntaxhighlight>

{{out}}
<pre style="height:15em;">
Bishop : BASAP
Carlson : CARLSA(N)
Carr : CAR
Chapman : CAPNAN
Franklin : FRANCL(AN)
Greene : GRAN
Harper : HARPAR
Jacobs : JACAB
Larson : LARSAN
Lawrence : LARANC
Lawson : LASAN
Louis, XVI : L
Lynch : LYNC
Mackenzie : MCANSY
Matthews : MAT
May jnr : MY
McCormack : MCARNA(C)
McDaniel : MCDANA(L)
McDonald : MCDANA(LD)
Mclaughlin : MCLAGL(AN)
Morrison : MARASA(N)
O'Banion : OBANAN
O'Brien : OBRAN
Richards : RACARD
Silva : SALV
Watkins : WATCAN
Xi : X
Wheeler : WALAR
Willis : WAL
brown, sr : BRAN
browne, III : BRAN
browne, IV : BRAN
knight : NAGT
mitchell : MATCAL
o'daniel : ODANAL
bevan : BAFAN
evans : EVAN
D'Souza : DSAS
Hoyle-Johnson : HAYLAJ(ANSAN)
Vaughan Williams : VAGANW(ALAN)
de Sousa : DASAS
de la Mare II : DALANA(R)
</pre>

=={{header|zkl}}==
{{trans|Python}}
<syntaxhighlight lang="zkl">fcn replaceAt(text,pos,fromList,toList){
foreach f,t in (fromList.zip(toList)){
if(0==text[pos,*].find(f)) return(text.set(pos,f.len(),t));
}
text
}
fcn replaceEnd(text,fromList,toList){
foreach f,t in (fromList.zip(toList)){
if ((text.len() - f.len())==text.rfind(f)) return(text.set(-f.len(),*,t));
}
text
}
fcn nysiis(name){
vowels := "AEIOU";
// name = name.filter(fcn(c){ (not c.isSpace()) }).toUpper();
name = name.toUpper().filter("matches","[A-Z]");
name = replaceAt(name,0, T("MAC", "KN", "K", "PH", "PF", "SCH"),
T("MCC", "N", "C", "FF", "FF", "SSS"));
name = replaceEnd(name,T("EE", "IE", "DT", "RT", "RD", "NT", "ND"),
T("Y", "Y", "D", "D", "D", "D", "D"));
key := name[0];
foreach i in ([1 .. name.len()-1]){
n_1,n,n1b:= name[i-1], name[i], name[i+1,1]; // "" if i+1>len
name = replaceAt(name,i, T("EV").extend(vowels.split("")),
T("AF","A","A","A","A","A"));
name = replaceAt(name,i, T("Q","Z","M"), T("G","S","N"));
name = replaceAt(name,i, T("KN", "K"), T("N", "C"));
name = replaceAt(name,i, T("SCH", "PH"), T("SSS", "FF"));
if (n=="H" and (not vowels.holds(n_1) or not vowels.holds(n1b)))
name = name.set(i,1,n_1);
if (n=="W" and vowels.holds(n_1)) name = name.set(i,1,"A");
if (key[-1,1] != name[i]) key += name[i];
}
replaceEnd(replaceEnd(replaceEnd(key, T("S"), T("")), T("AY"), T("Y")), T("A"), T(""));
}
</syntaxhighlight>
<syntaxhighlight lang="zkl">
names := T("Bishop", "Carlson", "Carr", "Chapman",
"Franklin", "Greene", "Harper", "Jacobs", "Larson", "Lawrence",
"Lawson", "Louis, XVI", "Lynch", "Mackenzie", "Matthews",
"McCormack", "McDaniel", "McDonald", "Mclaughlin", "Morrison",
"O'Banion", "O'Brien", "Richards", "Silva", "Watkins",
"Wheeler", "Willis", "brown, sr", "browne, III", "browne, IV",
"knight", "mitchell", "o'daniel");
foreach name in (names){ println("%11s: %s".fmt(name, nysiis(name))) }
</syntaxhighlight>
{{out}}
<pre style="height:15em;">
Bishop: BASAP
Carlson: CARLSAN
Carr: CAR
Chapman: CAPNAN
Franklin: FRANCLAN
Greene: GRAN
Harper: HARPAR
Jacobs: JACAB
Larson: LARSAN
Lawrence: LARANC
Lawson: LASAN
Louis, XVI: LASXV
Lynch: LYNC
Mackenzie: MCANSY
Matthews: MAT
McCormack: MCARNAC
McDaniel: MCDANAL
McDonald: MCDANALD
Mclaughlin: MCLAGLAN
Morrison: MARASAN
O'Banion: OBANAN
O'Brien: OBRAN
Richards: RACARD
Silva: SALV
Watkins: WATCAN
Wheeler: WALAR
Willis: WAL
brown, sr: BRANSR
browne, III: BRAN
browne, IV: BRANAV
knight: NAGT
mitchell: MATCAL
o'daniel: ODANAL
</pre>
</pre>

Latest revision as of 10:38, 4 August 2024

NYSIIS is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.
This page uses content from Wikipedia. The original article was at NYSIIS. The list of authors can be seen in the page history. As with Rosetta Code, the text of Wikipedia is available under the GNU FDL. (See links for details on variance)


The New York State Identification and Intelligence System phonetic code, commonly known as NYSIIS, is a phonetic algorithm for creating indices for words based on their pronunciation.

The goal is for homophones to be encoded to the same representation so that they can be matched despite minor differences in spelling.


Task

Implement the original NYSIIS algorithm, shown in Wikipedia, rather than any other subsequent modification.

Also, before the algorithm is applied the input string should be converted to upper case with all white space removed.

An optional step is to handle multiple names, including double-barrelled names or double surnames (e.g. 'Hoyle-Johnson' or 'Vaughan Williams') and unnecessary suffixes/honours that are not required for indexing purposes (e.g. 'Jnr', 'Sr', 'III', etc) - a small selection will suffice.

The original implementation is also restricted to six characters, but this is not a requirement.


Note

rules 7, 8 and 9 in the Wikipedia article are not mutually exclusive, Willis encodes to WAL, for example.

See also



11l

Translation of: Python
This example is incorrect. Please fix the code and remove this message.

Details: Willis should encode to WAL and Mathews to MAT - see the discussion page

V _vowels = ‘AEIOU’

F replace_at(String text; position, fromlist, tolist)
   L(f, t) zip(fromlist, tolist)
      I text[position .+ f.len] == f
         R text[0 .< position]‘’t‘’text[position + f.len ..]
   R text

F replace_end(String text; fromlist, tolist)
   L(f, t) zip(fromlist, tolist)
      I text.ends_with(f)
         R text[0 .< (len)-f.len]‘’t
   R text

F nysiis(String =name)
   name = name.replace(re:‘\W’, ‘’).uppercase()
   name = replace_at(name, 0, [‘MAC’, ‘KN’, ‘K’, ‘PH’, ‘PF’, ‘SCH’],
                              [‘MCC’, ‘N’,  ‘C’, ‘FF’, ‘FF’, ‘SSS’])
   name = replace_end(name, [‘EE’, ‘IE’, ‘DT’, ‘RT’, ‘RD’, ‘NT’, ‘ND’],
                            [‘Y’,  ‘Y’,  ‘D’,  ‘D’,  ‘D’,  ‘D’,  ‘D’])
   V key = String(name[0])
   V key1 = ‘’
   V i = 1
   L i < name.len
      V (n_1, n) = (name[i - 1], name[i])
      V n1_ = I i + 1 < name.len {name[i + 1]} E ‘’
      name = replace_at(name, i, [‘EV’] [+] Array(:_vowels).map(String), [‘AF’] [+] [String(‘A’)] * 5)
      name = replace_at(name, i, [String(‘Q’), ‘Z’, ‘M’], [String(‘G’), ‘S’, ‘N’])
      name = replace_at(name, i, [‘KN’, ‘K’], [String(‘N’), ‘C’])
      name = replace_at(name, i, [‘SCH’, ‘PH’], [‘SSS’, ‘FF’])
      I n == ‘H’ & (n_1 !C :_vowels | n1_ !C :_vowels)
         name = name[0 .< i]‘’n_1‘’name[i + 1 ..]
      I n == ‘W’ & n_1 C :_vowels
         name = name[0 .< i]‘A’name[i + 1 ..]
      I key != ‘’ & key.last != name[i]
         key ‘’= name[i]
      i++
   key = replace_end(key, [‘S’, ‘AY’, ‘A’], [‘’, ‘Y’, ‘’])
   R key1‘’key

V names = [‘Bishop’, ‘Carlson’, ‘Carr’, ‘Chapman’, ‘Franklin’,
           ‘Greene’, ‘Harper’, ‘Jacobs’, ‘Larson’, ‘Lawrence’,
           ‘Lawson’, ‘Louis, XVI’, ‘Lynch’, ‘Mackenzie’, ‘Matthews’,
           ‘McCormack’, ‘McDaniel’, ‘McDonald’, ‘Mclaughlin’, ‘Morrison’,
           ‘O'Banion’, ‘O'Brien’, ‘Richards’, ‘Silva’, ‘Watkins’,
           ‘Wheeler’, ‘Willis’, ‘brown, sr’, ‘browne, III’, ‘browne, IV’,
           ‘knight’, ‘mitchell’, ‘o'daniel’]
L(name) names
   print(‘#15: #.’.format(name, nysiis(name)))
Output:
         Bishop: BASAP
        Carlson: CARLSAN
           Carr: CAR
        Chapman: CAPNAN
       Franklin: FRANCLAN
         Greene: GRAN
         Harper: HARPAR
         Jacobs: JACAB
         Larson: LARSAN
       Lawrence: LARANC
         Lawson: LASAN
     Louis, XVI: LASXV
          Lynch: LYNC
      Mackenzie: MCANSY
       Matthews: MATA
      McCormack: MCARNAC
       McDaniel: MCDANAL
       McDonald: MCDANALD
     Mclaughlin: MCLAGLAN
       Morrison: MARASAN
       O'Banion: OBANAN
        O'Brien: OBRAN
       Richards: RACARD
          Silva: SALV
        Watkins: WATCAN
        Wheeler: WALAR
         Willis: WALA
      brown, sr: BRANSR
    browne, III: BRAN
     browne, IV: BRANAV
         knight: NAGT
       mitchell: MATCAL
       o'daniel: ODANAL

ALGOL 68

This produces the same results as the Apache Commons Nysiis codec, so Mathews yields MAT and Willis yields WAL.

BEGIN # NYSIIS algorithm                                                     #

    # returns str in upper-case with non-letters removed                     #
    OP   TOALPHAUPPER = ( STRING str )STRING:
         BEGIN
            STRING result := "";
            FOR i FROM LWB str TO UPB str DO
                CHAR c = str[ i ];
                IF   c >= "a" AND c <= "z"
                THEN result +:= REPR ( ABS c + ( ABS "A" - ABS "a" ) )
                ELIF c >= "A" AND c <= "Z"
                THEN result +:= c
                FI
            OD;
            result
         END # TOALPHAUPPER # ;
    # returns str with all characters in delete removed                      #
    PRIO REMOVE = 1;
    OP   REMOVE = ( STRING str, CHAR delete )STRING:
         BEGIN
            STRING result := "";
            FOR i FROM LWB str TO UPB str DO
                CHAR c = str[ i ];
                IF c /= delete THEN
                    result +:= c
                FI
            OD;
            result
         END # REMOVE # ;
    # returns str with with the character at pos removed                     #
    OP   REMOVE = ( STRING str, INT pos )STRING:
         IF pos < LWB str OR pos > UPB str
         THEN str
         ELIF pos = LWB str
         THEN str[ LWB str + 1 : ]
         ELIF pos = UPB str
         THEN str[ : LENGTH str - 1 ]
         ELSE str[ : pos - 1 ] + str[ pos + 1 : ]
         FI # REMOVE # ;
    # returns the length of str                                              #
    OP   LENGTH = ( STRING str )INT: ( UPB str - LWB str ) + 1;
    # returns TRUE is str starts with prefix, FALSE otherwise                #
    PRIO STARTSWITH = 9;
    OP   STARTSWITH = ( STRING str, prefix )BOOL:
         IF INT p len = LENGTH prefix;
            p len > LENGTH str
         THEN FALSE
         ELSE str[ LWB str : LWB str + p len - 1 ] = prefix
         FI # STARTSWITH # ;
    OP   STARTSWITH = ( STRING str, CHAR prefix )BOOL: str STARTSWITH STRING(prefix);
    # returns TRUE if c is a vowel (AEIOU), FALSE otherwise                  #
    OP   VOWEL = ( CHAR c )BOOL: c = "A" OR c = "E" OR c = "I" OR c = "O" OR c = "U";
    # returns the character from str following pos, if there is one,         #
    #         REPR 0 otherwise                                               #
    PRIO FOLLOWING = 9;
    OP   FOLLOWING = ( STRING str, INT pos )CHAR: IF pos < UPB str THEN str[ pos + 1 ] ELSE REPR 0 FI;
    # left pads str to at least w charaters                                  #
    PRIO PAD = 9;
    OP   PAD = ( INT w, STRING str )STRING:
         IF LENGTH str >= w THEN str ELSE ( ( w - LENGTH str ) * " " ) + str FI;
    
    # returns the NYSIIS code of name                                        #
    OP   NYSIIS = ( STRING name )STRING:
         BEGIN
            STRING cname := ( TOALPHAUPPER ( name REMOVE " " ) );
            IF   cname STARTSWITH "MAC" THEN cname[ 1 : 3 ] := "MCC"
            ELIF cname STARTSWITH "KN"  THEN cname[ 1 : 2 ] := "NN"
            ELIF cname STARTSWITH "K"   THEN cname[ 1     ] := "C"
            ELIF cname STARTSWITH "PH"  THEN cname[ 1 : 2 ] := "FF"
            ELIF cname STARTSWITH "PF"  THEN cname[ 1 : 2 ] := "FF"
            ELIF cname STARTSWITH "SCH" THEN cname[ 1 : 3 ] := "SSS"
            FI;
            INT    r len  := LENGTH cname;
            STRING suffix = cname[ r len - 1 : r len ];
            IF   suffix = "EE" OR suffix = "IE" THEN cname[ r len - 1 : ] := "Y "
            ELIF suffix = "DT" OR suffix = "RT" OR suffix = "RD"
              OR suffix = "NT" OR suffix = "ND" THEN cname[ r len - 1 : ] := "D "
            FI;
            INT r pos := 2;
            WHILE IF r pos > r len THEN FALSE ELSE cname[ r pos ] /= " " FI DO
                CHAR p = cname[ r pos - 1 ];
                CHAR c = cname[ r pos     ];
                CHAR n = cname FOLLOWING r pos;
                IF c = "E" THEN
                    IF n = "V" THEN cname[ r pos : r pos + 1 ] := "AF" ELSE cname[ r pos ] := "A" FI
                ELIF VOWEL c THEN cname[ r pos ] := "A"
                ELIF c = "Q" THEN cname[ r pos ] := "G"
                ELIF c = "Z" THEN cname[ r pos ] := "S"
                ELIF c = "M" THEN cname[ r pos ] := "N"
                ELIF IF r pos >= r len - 1 THEN FALSE ELSE cname[ r pos : r pos + 2 ] = "SCH" FI
                THEN cname[ r pos : r pos + 2 ] := "SSS"
                ELIF c = "P" AND n = "H" THEN cname[ r pos : r pos + 1 ] := "FF"
                ELIF c = "K" THEN cname[ r pos ] := IF n = "N" THEN "N" ELSE "C" FI
                ELIF c = "H"
                 AND ( NOT VOWEL p OR NOT VOWEL n ) THEN cname[ r pos ] := p
                ELIF c = "W" AND VOWEL p
                THEN cname[ r pos ] := p
                FI;
                IF cname[ r pos ] = p
                THEN cname := cname REMOVE r pos; r len -:= 1
                ELSE r pos +:= 1
                FI
            OD;
            IF cname[ r len ] = " " THEN cname := cname REMOVE r len; r len -:= 1 FI;
            IF IF r len < 1 THEN FALSE ELSE c name[ r len ] = "S" FI
            THEN cname := cname REMOVE r len;
                 r len -:= 1
            FI;
            IF IF r len < 2 THEN FALSE ELSE c name[ r len - 1 : ] = "AY" FI
            THEN cname := cname REMOVE r len;
                 cname[ r len -:= 1 ] := "Y"
            FI;
            IF IF r len < 1 THEN FALSE ELSE c name[ r len ] = "A" FI
            THEN cname := cname REMOVE r len;
                 r len -:= 1
            FI;
            cname
         END # NYSIIS # ;

    # test cases as used in other samples, e.g. 11l                          #
    [,]STRING tests = ( (      "Bishop", "BASAP"    )
                      , (     "Carlson", "CARLSAN"  )
                      , (        "Carr", "CAR"      )
                      , (     "Chapman", "CAPNAN"   )
                      , (    "Franklin", "FRANCLAN" )
                      , (      "Greene", "GRAN"     )
                      , (      "Harper", "HARPAR"   )
                      , (      "Jacobs", "JACAB"    )
                      , (      "Larson", "LARSAN"   )
                      , (    "Lawrence", "LARANC"   )
                      , (      "Lawson", "LASAN"    )
                      , (  "Louis, XVI", "LASXV"    )
                      , (       "Lynch", "LYNC"     )
                      , (   "Mackenzie", "MCANSY"   )
                      , (    "Matthews", "MAT"      )
                      , (   "McCormack", "MCARNAC"  )
                      , (    "McDaniel", "MCDANAL"  )
                      , (    "McDonald", "MCDANALD" )
                      , (  "Mclaughlin", "MCLAGLAN" )
                      , (    "Morrison", "MARASAN"  )
                      , (    "O'Banion", "OBANAN"   )
                      , (     "O'Brien", "OBRAN"    )
                      , (    "Richards", "RACARD"   )
                      , (       "Silva", "SALV"     )
                      , (     "Watkins", "WATCAN"   )
                      , (     "Wheeler", "WALAR"    )
                      , (      "Willis", "WAL"      )
                      , (   "brown, sr", "BRANSR"   )
                      , ( "browne, III", "BRAN"     )
                      , (  "browne, IV", "BRANAV"   )
                      , (       "hayes", "HAY"      )
                      , (      "knight", "NAGT"     )
                      , (    "mitchell", "MATCAL"   )
                      , (    "o'daniel", "ODANAL"   )
                      );
    FOR t FROM 1 LWB tests TO 1 UPB tests DO
        STRING name     = tests[ t, 1 ];
        STRING code     = NYSIIS name;
        STRING expected = tests[ t, 2 ];
        print( ( 12 PAD name, " -> ", code
               , IF code = expected
                 THEN ""
                 ELSE "    !! expected " + expected
                 FI
               , newline
               )
             )
    OD
END
Output:
      Bishop -> BASAP
     Carlson -> CARLSAN
        Carr -> CAR
     Chapman -> CAPNAN
    Franklin -> FRANCLAN
      Greene -> GRAN
      Harper -> HARPAR
      Jacobs -> JACAB
      Larson -> LARSAN
    Lawrence -> LARANC
      Lawson -> LASAN
  Louis, XVI -> LASXV
       Lynch -> LYNC
   Mackenzie -> MCANSY
    Matthews -> MAT
   McCormack -> MCARNAC
    McDaniel -> MCDANAL
    McDonald -> MCDANALD
  Mclaughlin -> MCLAGLAN
    Morrison -> MARASAN
    O'Banion -> OBANAN
     O'Brien -> OBRAN
    Richards -> RACARD
       Silva -> SALV
     Watkins -> WATCAN
     Wheeler -> WALAR
      Willis -> WAL
   brown, sr -> BRANSR
 browne, III -> BRAN
  browne, IV -> BRANAV
       hayes -> HAY
      knight -> NAGT
    mitchell -> MATCAL
    o'daniel -> ODANAL

C++

Implementation based on Wikipedia description of the algorithm.

#include <iostream>   // required for debug code in main() only
#include <iomanip>    // required for debug code in main() only
#include <string>
#include <cstring>

std::string NYSIIS( std::string const& str )
{
    std::string s, out;
    s.reserve( str.length() );
    for( auto const c : str )
    {
        if( c >= 'a' && c <= 'z' )
            s += c - ('a' - 'A');
        else if( c >= 'A' && c <= 'Z' )
            s += c;
    }

    auto replace = []( char const * const from, char const* to, char* const dst ) -> bool
    {
        auto const n = strlen( from );
        if( strncmp( from, dst, n ) == 0 )
        {
            strncpy( dst, to, n );
            return true;
        }
        return false;
    };

    auto multiReplace = []( char const* const* from, char const* to, char* const dst ) -> bool
    {
        auto const n = strlen( *from );
        for( ; *from; ++from )
            if( strncmp( *from, dst, n ) == 0 )
            {
                memcpy( dst, to, n );
                return true;
            }
        return false;
    };

    auto isVowel = []( char const c ) -> bool
    {
        return c == 'A' || c == 'E' || c == 'I' || c == 'O' || c == 'U';
    };

    size_t n = s.length();
    replace( "MAC", "MCC", &s[0] );
    replace( "KN", "NN", &s[0] );
    replace( "K", "C", &s[0] );
    char const* const prefix[] = { "PH", "PF", 0 };
    multiReplace( prefix, "FF", &s[0] );
    replace( "SCH", "SSS", &s[0] );

    char const* const suffix1[] = { "EE", "IE", 0 };
    char const* const suffix2[] = { "DT", "RT", "RD", "NT", "ND", 0 };
    if( multiReplace( suffix1, "Y", &s[n - 2] ) || multiReplace( suffix2, "D", &s[n - 2] ))
    {
        s.pop_back();
        --n;
    }

    out += s[0];

    char* vowels[] = { "A", "E", "I", "O", "U", 0 };
    for( unsigned i = 1; i < n; ++i )
    {
        char* const c = &s[i];
        if( !replace( "EV", "AV", c ) )
            multiReplace( vowels, "A", c );
        replace( "Q", "G", c );
        replace( "Z", "S", c );
        replace( "M", "N", c );
        if( !replace( "KN", "NN", c ))
            replace( "K", "C", c );
        replace( "SCH", "SSS", c );
        replace( "PH", "FF", c );
        if( *c == 'H' && (!isVowel( s[i - 1] ) || i + 1 >= n || !isVowel( s[i + 1] )))
            *c = s[i - 1];
        if( *c == 'W' && isVowel( s[i - 1] ))
            *c = 'A';
        if( out.back() != *c )
            out += *c;
    }

    if( out.back() == 'S')
        out.pop_back();
    n = out.length() - 2;
    if( out[n] == 'A' && out[n + 1] == 'Y' )
        out = out.substr( 0, n ) + "Y";
    if( out.back() == 'A' )
        out.pop_back();
    return out;
}

int main()
{
    static char const * const names[][2] = {
    { "Bishop", "BASAP" },
    { "Carlson", "CARLSAN" },
    { "Carr", "CAR" },
    { "Chapman", "CAPNAN" },
    { "Franklin", "FRANCLAN" },
    { "Greene", "GRAN" },
    { "Harper", "HARPAR" },
    { "Jacobs", "JACAB" },
    { "Larson", "LARSAN" },
    { "Lawrence", "LARANC" },
    { "Lawson", "LASAN" },
    { "Louis, XVI", "LASXV" },
    { "Lynch", "LYNC" },
    { "Mackenzie", "MCANSY" },
    { "Matthews", "MAT" },
    { "McCormack", "MCARNAC" },
    { "McDaniel", "MCDANAL" },
    { "McDonald", "MCDANALD" },
    { "Mclaughlin", "MCLAGLAN" },
    { "Morrison", "MARASAN" },
    { "O'Banion", "OBANAN" },
    { "O'Brien", "OBRAN" },
    { "Richards", "RACARD" },
    { "Silva", "SALV" },
    { "Watkins", "WATCAN" },
    { "Wheeler", "WALAR" },
    { "Willis", "WAL" },
    { "brown, sr", "BRANSR" },
    { "browne, III", "BRAN" },
    { "browne, IV", "BRANAV" },
    { "knight", "NAGT" },
    { "mitchell", "MATCAL" },
    { "o'daniel", "ODANAL" } };

    for( auto const& name : names )
    {
        auto const code = NYSIIS( name[0] );
        std::cout << std::left << std::setw( 16 ) << name[0] << std::setw( 8 ) << code;
        if( code == std::string( name[1] ))
            std::cout << " ok";
        else
            std::cout << " ERROR: " << name[1] << " expected";
        std::cout << std::endl;
    }

    return 0;
}
Example output:
 
Bishop          BASAP    ok
Carlson         CARLSAN  ok
Carr            CAR      ok
Chapman         CAPNAN   ok
Franklin        FRANCLAN ok
Greene          GRAN     ok
Harper          HARPAR   ok
Jacobs          JACAB    ok
Larson          LARSAN   ok
Lawrence        LARANC   ok
Lawson          LASAN    ok
Louis, XVI      LASXV    ok
Lynch           LYNC     ok
Mackenzie       MCANSY   ok
Matthews        MAT      ok
McCormack       MCARNAC  ok
McDaniel        MCDANAL  ok
McDonald        MCDANALD ok
Mclaughlin      MCLAGLAN ok
Morrison        MARASAN  ok
O'Banion        OBANAN   ok
O'Brien         OBRAN    ok
Richards        RACARD   ok
Silva           SALV     ok
Watkins         WATCAN   ok
Wheeler         WALAR    ok
Willis          WAL      ok
brown, sr       BRANSR   ok
browne, III     BRAN     ok
browne, IV      BRANAV   ok
knight          NAGT     ok
mitchell        MATCAL   ok
o'daniel        ODANAL   ok

Caché ObjectScript

Refactored code based on other examples to reduce footprint.

Class Utils.Phonetic [ Abstract ]
{

ClassMethod EncodeName(pAlgorithm As %String = "", pName As %String = "", Output pCode As %String, pSuffixRem As %Boolean = 1, pTruncate As %Integer = 0) As %Status
{
	// check algorithm and name
	Set pAlgorithm=$ZConvert(pAlgorithm, "l")
	If pAlgorithm="" Quit $$$ERROR($$$GeneralError, "No algorithm specified.")
	If $Case(pAlgorithm, "nysiis":1, :0)=0 Quit $$$ERROR($$$GeneralError, "Unknown algorithm specified.")
	If $Match(pName, ".*\d.*# no numbers") Quit $$$ERROR($$$GeneralError, "Name cannot contain numerics.")
	
	// remove apostrophes, find punctuation and replace with spaces (exclude hyphens)
	Set pName=$Translate(pName, "'")
	Set pun=$ZStrip(pName, "*E'P", "-")
	Set pName=$Translate(pName, pun, $Justify(" ", $Length(pun)))
	
	// convert name(s) to uppercase and remove all white space
	Set pName=$ZStrip($ZConvert(pName, "U"), "<=>W")
	
	// remove suffixes (e.g. 'Jnr', 'OBE', 'DSC', etc), including roman numerals (e.g. 'II', 'VIII')
	// - http://en.wikipedia.org/wiki/List_of_post-nominal_letters_(United_Kingdom)
	If pSuffixRem {
		Set ords=$ListBuild("KG", "LG", "KT", "LT", "GCB", "KCB", "DCB", "CB", "GCMG", "KCMG", "DCMG", "CMG", "DSO", 
			"GCVO", "KCVO", "DCVO", "CVO", "LVO", "MVO", "OM", "ISO", "GBE", "KBE", "DBE", "CBE", "OBE", "MBE", "CH")
		Set decs=$ListBuild("VC", "GC", "CGC", "RRC", "DSC", "MC", "DFC", "AFC", "ARRC", "OBI", "IOM")
		Set regexp="( )(SNR|SR|JNR|JR|ESQ|"_$ListToString(ords, "|")_"|"_$ListToString(decs, "|")_"|[IVX]+)"
		Set rem=##class(%Regex.Matcher).%New(regexp, pName)
		Set pName=rem.ReplaceAll("")
	}
	
	// replace hyphen and white space, plus some final validation
	Set pName=$ZStrip($Translate(pName, "-", " "), "<=>W")
	If $Length($Piece(pName, " "))<2 Quit $$$ERROR($$$GeneralError, "Invalid name.")
	
	// begin algorithm and truncate result, if necessary
	Set pCode=""
	For piece=1:1:$Length(pName, " ") {
		If pAlgorithm="nysiis" Set pCode=pCode_..ToNYSIIS($Piece(pName, " ", piece))
	}
	If pTruncate {
		Set pName=pCode
		Set pCode=$Extract(pCode, 1, pTruncate)
		Set $Extract(pName, 1, pTruncate)=""
		If $Length(pName) Set pCode=pCode_"["_pName_"]"
	}
	
	// finished
	Quit $$$OK
}

ClassMethod ToNYSIIS(pName As %String) As %String
{
	/*
		New York State Identification and Intelligence System (NYSIIS) Phonetic Encoder
		- http://en.wikipedia.org/wiki/New_York_State_Identification_and_Intelligence_System
		- http://www.dropby.com/indexLF.html?content=/NYSIIS.html
	*/
	
	// create regexp matcher instance, remove punctuation and convert all to upper case
	Set rem=##class(%Regex.Matcher).%New(" ")
	Set rem.Text=$ZConvert($ZStrip(pName, "*P"), "U")
	
	// translate first characters of name:
	// => MAC->MCC, KN->N, K->C, PH/PF->FF, SCH->SSS
	For rule="^MAC->MCC", "^KN->N", "^K->C", "^(PH|PF)->FF", "^SCH->SSS" {
		Set rem.Pattern=$Piece(rule, "->")
		If rem.Locate() Set rem.Text=rem.ReplaceFirst($Piece(rule, "->", 2)) Quit
	}

	// translate last characters of name:
	// => EE/IE->Y, DT/RT/RD/NT/ND->D
	For rule="(EE|IE)$->Y", "(DT|RT|RD|NT|ND)$->D" {
		Set rem.Pattern=$Piece(rule, "->")
		If rem.Locate() Set rem.Text=rem.ReplaceFirst($Piece(rule, "->", 2)) Quit
	}
	
	// first character of key = first character of name
	Set pName1=$Extract(rem.Text, 1), rem.Text=$Extract(rem.Text, 2, *)
	
	// translate remaining characters by following rules, incrementing by one character each time:
	// => EV->AF else A,E,I,O,U->A
	// => Q->G, Z->S, M->N
	// => KN->N else K->C
	// => SCH->SSS, PH->FF
	// => H->if previous or next is non-vowel, previous
	// => W->if previous is vowel, A (A is the only vowel left)
	// => add current to key if current is not same as the last key character
	Set ptr=0, rules=$ListBuild("EV->AF", "(A|E|I|O|U)->A", "Q->G", "Z->S", "M->N", "KN->N", "K->C", 
		"SCH->SSS", "PH->FF", "H[^A]", "[^A]H", "AW->A")
	While $ListNext(rules, ptr, rule) {
		Set rem.Pattern=$Piece(rule, "->")
		If $Piece(rule, "->", 2)="",  rem.Locate() {
			Set $Piece(rule, "->", 2)=$Translate(rem.Group, "H")
		}
		Set rem.Text=rem.ReplaceAll($Piece(rule, "->", 2))
	}
	Set pName=$ZStrip(rem.Text, "=U")  // remove duplicates
	
	// if last character is S, remove it
	If $Extract(pName, *)="S" Set pName=$Extract(pName, 1, *-1)
	
	// if last characters are AY, replace with Y
	If $Extract(pName, *-1, *)="AY" Set pName=$Extract(pName, 1, *-2)_"Y"
	
	// if last character is A, remove it
	If $Extract(pName, *)="A" Set pName=$Extract(pName, 1, *-1)
	
	// append translated key to removed first character
	Quit pName1_pName
}

}
Examples:
USER>For  { Read !, name Quit:name=""  Set sc=##class(Utils.Phonetic).EncodeName("nysiis", name, .code,, 6) If sc Write " -> ", code }

knight -> NAGT
mitchell -> MATCAL
o'daniel -> ODANAL
brown sr -> BRAN
browne III -> BRAN
browne IV -> BRAN
O'Banion -> OBANAN
Mclaughlin -> MCLAGL[AN]
McCormack -> MCARNA[C]
Chapman -> CHAPNA[N]
Silva -> SALV
McDonald -> MCDANA[LD]
Lawson -> LASAN
Jacobs -> JACAB
Greene -> GRAN
O'Brien -> OBRAN
Morrison -> MARASA[N]
Larson -> LARSAN
Willis -> WAL
Mackenzie -> MCANSY
Carr -> CAR
Lawrence -> LARANC
Matthews -> MAT
Richards -> RACARD
Bishop -> BASAP
Franklin -> FRANCL[AN]
McDaniel -> MCDANA[L]
Harper -> HARPAR
Lynch -> LYNC
Watkins -> WATCAN
Carlson -> CARLSA[N]
Wheeler -> WHALAR
Louis XVI -> L
Hoyle-Johnson -> HAYLJA[NSAN]
Vaughan Williams -> VAGANW[ALAN]
D'Souza -> DSAS
de Sousa -> DSAS

D

Translation of: Python
import std.stdio, std.regex, std.algorithm, std.range, std.string;

string replaceAt(in string text, in uint pos, in string[] fromList,
                 in string[] toList) pure /*nothrow*/ @safe {
    foreach (const f, const t; zip(fromList, toList))
        if (text[pos .. $].startsWith(f))
            return [text[0 .. pos], t, text[pos + f.length .. $]].join;
    return text;
}

string replaceEnd(in string text, in string[] fromList,
                  in string[] toList) pure /*nothrow*/ @safe {
    foreach (const f, const t; zip(fromList, toList))
        if (text.endsWith(f))
            return text[0 .. $ - f.length] ~ t;
    return text;
}

string nysiis(string name) /*pure nothrow*/ @safe {
    enum vowels = "AEIOU";
    name = name.replaceAll(r"\W".regex, "").toUpper;
    name = name.replaceAt(0, ["MAC", "KN", "K", "PH", "PF", "SCH"],
                             ["MCC", "N",  "C", "FF", "FF", "SSS"]);
    name = name.replaceEnd(["EE", "IE", "DT", "RT", "RD", "NT", "ND"],
                           ["Y",  "Y",  "D",  "D",  "D",  "D",  "D"]);
    string key = name[0 .. 1];
    string key1;

    foreach (immutable i; 1 .. name.length) {
        immutable n_1 = name[i - 1 .. i];
        immutable n = name[i];
        immutable n1b = (i + 1 < name.length) ? name[i+1 .. i+2] : "";
        name = name.replaceAt(i, ["EV"] ~ vowels.split(""), ["AF"] ~
                              ["A"].replicate(5));
        name = name.replaceAt(i, "QZM".split(""), "GSN".split(""));
        name = name.replaceAt(i, ["KN", "K"], ["N", "C"]);
        name = name.replaceAt(i, ["SCH", "PH"], ["SSS", "FF"]);
        if (n == 'H' && (!vowels.canFind(n_1) || !vowels.canFind(n1b)))
            name = [name[0 .. i], n_1, name[i + 1 .. $]].join;
        if (n == 'W' && vowels.canFind(n_1))
            name = [name[0 .. i], "A", name[i + 1 .. $]].join;
        if (!key.empty && key[$ - 1] != name[i])
            key ~= name[i];
    }

    key = replaceEnd(key, ["S"], [""]);
    key = replaceEnd(key, ["AY"], ["Y"]);
    return key1 ~ replaceEnd(key, ["A"], [""]);
}

void main() @safe {
    immutable names = ["Bishop", "Carlson", "Carr", "Chapman",
        "Franklin", "Greene", "Harper", "Jacobs", "Larson", "Lawrence",
        "Lawson", "Louis, XVI", "Lynch", "Mackenzie", "Matthews",
         "McCormack", "McDaniel", "McDonald", "Mclaughlin", "Morrison",
         "O'Banion", "O'Brien", "Richards", "Silva", "Watkins",
         "Wheeler", "Willis", "brown, sr", "browne, III", "browne, IV",
         "knight", "mitchell", "o'daniel"];

    foreach (immutable name; names)
        writefln("%11s: %s", name, name.nysiis);
}
Output:
     Bishop: BASAP
    Carlson: CARLSAN
       Carr: CAR
    Chapman: CAPNAN
   Franklin: FRANCLAN
     Greene: GRAN
     Harper: HARPAR
     Jacobs: JACAB
     Larson: LARSAN
   Lawrence: LARANC
     Lawson: LASAN
 Louis, XVI: LASXV
      Lynch: LYNC
  Mackenzie: MCANSY
   Matthews: MAT
  McCormack: MCARNAC
   McDaniel: MCDANAL
   McDonald: MCDANALD
 Mclaughlin: MCLAGLAN
   Morrison: MARASAN
   O'Banion: OBANAN
    O'Brien: OBRAN
   Richards: RACARD
      Silva: SALV
    Watkins: WATCAN
    Wheeler: WALAR
     Willis: WAL
  brown, sr: BRANSR
browne, III: BRAN
 browne, IV: BRANAV
     knight: NAGT
   mitchell: MATCAL
   o'daniel: ODANAL

Go

Translation of: Kotlin
package main

import (
    "fmt"
    "strings"
)

type pair struct{ first, second string }

var (
    fStrs = []pair{{"MAC", "MCC"}, {"KN", "N"}, {"K", "C"}, {"PH", "FF"},
        {"PF", "FF"}, {"SCH", "SSS"}}
    lStrs = []pair{{"EE", "Y"}, {"IE", "Y"}, {"DT", "D"}, {"RT", "D"},
        {"RD", "D"}, {"NT", "D"}, {"ND", "D"}}
    mStrs = []pair{{"EV", "AF"}, {"KN", "N"}, {"SCH", "SSS"}, {"PH", "FF"}}
    eStrs = []string{"JR", "JNR", "SR", "SNR"}
)

func isVowel(b byte) bool {
    return strings.ContainsRune("AEIOU", rune(b))
}

func isRoman(s string) bool {
    if s == "" {
        return false
    }
    for _, r := range s {
        if !strings.ContainsRune("IVX", r) {
            return false
        }
    }
    return true
}

func nysiis(word string) string {
    if word == "" {
        return ""
    }
    w := strings.ToUpper(word)
    ww := strings.FieldsFunc(w, func(r rune) bool {
        return r == ' ' || r == ','
    })
    if len(ww) > 1 {
        last := ww[len(ww)-1]
        if isRoman(last) {
            w = w[:len(w)-len(last)]
        }
    }
    for _, c := range " ,'-" {
        w = strings.Replace(w, string(c), "", -1)
    }
    for _, eStr := range eStrs {
        if strings.HasSuffix(w, eStr) {
            w = w[:len(w)-len(eStr)]
        }
    }
    for _, fStr := range fStrs {
        if strings.HasPrefix(w, fStr.first) {
            w = strings.Replace(w, fStr.first, fStr.second, 1)
        }
    }
    for _, lStr := range lStrs {
        if strings.HasSuffix(w, lStr.first) {
            w = w[:len(w)-2] + lStr.second
        }
    }
    initial := w[0]
    var key strings.Builder
    key.WriteByte(initial)
    w = w[1:]
    for _, mStr := range mStrs {
        w = strings.Replace(w, mStr.first, mStr.second, -1)
    }
    sb := []byte{initial}
    sb = append(sb, w...)
    le := len(sb)
    for i := 1; i < le; {
        switch sb[i] {
        case 'E', 'I', 'O', 'U':
            sb[i] = 'A'
        case 'Q':
            sb[i] = 'G'
        case 'Z':
            sb[i] = 'S'
        case 'M':
            sb[i] = 'N'
        case 'K':
            sb[i] = 'C'
        case 'H':
            if !isVowel(sb[i-1]) || (i < le-1 && !isVowel(sb[i+1])) {
                sb[i] = sb[i-1]
            }
        case 'W':
            if isVowel(sb[i-1]) {
                sb[i] = 'A'
            }
        }
        if sb[i] == sb[i-1] {
            sb = append(sb[:i], sb[i+1:]...)
            le--
        } else {
            i++
        }
    }
    if sb[le-1] == 'S' {
        sb = sb[:le-1]
        le--
    }
    if le > 1 && string(sb[le-2:]) == "AY" {
        sb = sb[:le-2]
        sb = append(sb, 'Y')
        le--
    }
    if le > 0 && sb[le-1] == 'A' {
        sb = sb[:le-1]
        le--
    }
    prev := initial
    for j := 1; j < le; j++ {
        c := sb[j]
        if prev != c {
            key.WriteByte(c)
            prev = c
        }
    }
    return key.String()
}

func main() {
    names := []string{
        "Bishop", "Carlson", "Carr", "Chapman",
        "Franklin", "Greene", "Harper", "Jacobs", "Larson", "Lawrence",
        "Lawson", "Louis, XVI", "Lynch", "Mackenzie", "Matthews", "May jnr",
        "McCormack", "McDaniel", "McDonald", "Mclaughlin", "Morrison",
        "O'Banion", "O'Brien", "Richards", "Silva", "Watkins", "Xi",
        "Wheeler", "Willis", "brown, sr", "browne, III", "browne, IV",
        "knight", "mitchell", "o'daniel", "bevan", "evans", "D'Souza",
        "Hoyle-Johnson", "Vaughan Williams", "de Sousa", "de la Mare II",
    }
    for _, name := range names {
        name2 := nysiis(name)
        if len(name2) > 6 {
            name2 = fmt.Sprintf("%s(%s)", name2[:6], name2[6:])
        }
        fmt.Printf("%-16s : %s\n", name, name2)
    }
}
Output:
Bishop           : BASAP
Carlson          : CARLSA(N)
Carr             : CAR
Chapman          : CAPNAN
Franklin         : FRANCL(AN)
Greene           : GRAN
Harper           : HARPAR
Jacobs           : JACAB
Larson           : LARSAN
Lawrence         : LARANC
Lawson           : LASAN
Louis, XVI       : L
Lynch            : LYNC
Mackenzie        : MCANSY
Matthews         : MAT
May jnr          : MY
McCormack        : MCARNA(C)
McDaniel         : MCDANA(L)
McDonald         : MCDANA(LD)
Mclaughlin       : MCLAGL(AN)
Morrison         : MARASA(N)
O'Banion         : OBANAN
O'Brien          : OBRAN
Richards         : RACARD
Silva            : SALV
Watkins          : WATCAN
Xi               : X
Wheeler          : WALAR
Willis           : WAL
brown, sr        : BRAN
browne, III      : BRAN
browne, IV       : BRAN
knight           : NAGT
mitchell         : MATCAL
o'daniel         : ODANAL
bevan            : BAFAN
evans            : EVAN
D'Souza          : DSAS
Hoyle-Johnson    : HAYLAJ(ANSAN)
Vaughan Williams : VAGANW(ALAN)
de Sousa         : DASAS
de la Mare II    : DALANA(R)

Java

Hand written codec

Works with: Java version 8
import static java.util.Arrays.*;
import static java.util.Arrays.*;
import static java.lang.System.out;

public class NYSIIS {

    final static String[][] first = {{"MAC", "MCC"}, {"KN", "N"}, {"K", "C"},
    {"PH", "FF"}, {"PF", "FF"}, {"SCH", "SSS"}};

    final static String[][] last = {{"EE", "Y"}, {"IE", "Y"}, {"DT", "D"},
    {"RT", "D"}, {"RD", "D"}, {"NT", "D"}, {"ND", "D"}};

    final static String Vowels = "AEIOU";

    public static void main(String[] args) {
        stream(args).parallel().map(n -> transcode(n)).forEach(out::println);
    }

    static String transcode(String s) {
        int len = s.length();
        StringBuilder sb = new StringBuilder(len);

        for (int i = 0; i < len; i++) {
            char c = s.charAt(i);
            if (c >= 'a' && c <= 'z')
                sb.append((char) (c - 32));
            else if (c >= 'A' && c <= 'Z')
                sb.append(c);
        }

        replace(sb, 0, first);
        replace(sb, sb.length() - 2, last);

        len = sb.length();
        sb.append(" ");
        for (int i = 1; i < len; i++) {
            char prev = sb.charAt(i - 1);
            char curr = sb.charAt(i);
            char next = sb.charAt(i + 1);

            if (curr == 'E' && next == 'V')
                sb.replace(i, i + 2, "AF");

            else if (isVowel(curr))
                sb.setCharAt(i, 'A');

            else if (curr == 'Q')
                sb.setCharAt(i, 'G');

            else if (curr == 'Z')
                sb.setCharAt(i, 'S');

            else if (curr == 'M')
                sb.setCharAt(i, 'N');

            else if (curr == 'K' && next == 'N')
                sb.setCharAt(i, 'N');

            else if (curr == 'K')
                sb.setCharAt(i, 'C');

            else if (sb.indexOf("SCH", i) == i)
                sb.replace(i, i + 3, "SSS");

            else if (curr == 'P' && next == 'H')
                sb.replace(i, i + 2, "FF");

            else if (curr == 'H' && (!isVowel(prev) || !isVowel(next)))
                sb.setCharAt(i, prev);

            else if (curr == 'W' && isVowel(prev))
                sb.setCharAt(i, prev);

            if (sb.charAt(i) == prev) {
                sb.deleteCharAt(i--);
                len--;
            }
        }
        sb.setLength(sb.length() - 1); // We've added a space

        int lastPos = sb.length() - 1;
        if (lastPos > 1) {

            if (sb.charAt(lastPos) == 'S') {
                sb.setLength(lastPos);
                lastPos --;
            }
            if (sb.lastIndexOf("AY") == lastPos - 1) {
                sb.delete(lastPos - 1, lastPos + 1).append("Y");
                lastPos --;
            }
            if (sb.charAt(lastPos) == 'A') {
                sb.setLength(lastPos);
                lastPos --;
            }
        }

        if (sb.length() > 6)
            sb.insert(6, '[').append(']');

        return String.format("%s -> %s", s, sb);
    }

    private static void replace(StringBuilder sb, int start, String[][] maps) {
        if (start >= 0)
            for (String[] map : maps) {
                if (sb.indexOf(map[0]) == start) {
                    sb.replace(start, start + map[0].length(), map[1]);
                    break;
                }
            }
    }

    private static boolean isVowel(char c) {
        return Vowels.indexOf(c) != -1;
    }
}
Bishop -> BASAP
browneIII -> BRAN
browneIV -> BRANAV
brownsr -> BRANSR
Carlson -> CARLSA[N]
Carr -> CAR
Chapman -> CAPNAN
Franklin -> FRANCL[AN]
Greene -> GRAN
Harper -> HARPAR
Hayes -> HAY
Jacobs -> JACAB
knight -> NAGT
Larson -> LARSAN
Lawrence -> LARANC
Lawson -> LASAN
LouisXVI -> LASXV
Lynch -> LYNC
Mackenzie -> MCANSY
Matthews -> MAT
McCormack -> MCARNA[C]
McDaniel -> MCDANA[L]
McDonald -> MCDANA[LD]
Mclaughlin -> MCLAGL[AN]
mitchell -> MATCAL
Morrison -> MARASA[N]
O'Banion -> OBANAN
O'Brien -> OBRAN
o'daniel -> ODANAL
Richards -> RACARD
Silva -> SALV
Watkins -> WATCAN
Wheeler -> WALAR
Willis -> WAL

Using a library

This uses the Apache Commons NYSIIS codec.

import org.apache.commons.codec.language.Nysiis;
public class nysiisUsingApache
{
    /** NYSIIS codec that wiil allow strings longer than 6 characters
      * to be generated
      */
    private static Nysiis codec = new Nysiis( false );

    /** Encodes name using the codec and checks the result is encoded.
      */
    private static void test( String name, String encoded )
    {
        String result = (String) codec.encode( name );
        System.out.print( String.format( "%16s", name ) + " -> " + result );
        if( result.compareTo( encoded ) != 0 )
        {
            System.out.print( " -- expected \"" + encoded + "\"" );
        } // if result.compareTo( encoded ) != 0
        System.out.println();
    } // test

    public static void main( String[] args )
    {
        test(      "Bishop", "BASAP"    );
        test(     "Carlson", "CARLSAN"  );
        test(        "Carr", "CAR"      );
        test(     "Chapman", "CAPNAN"   );
        test(    "Franklin", "FRANCLAN" );
        test(      "Greene", "GRAN"     );
        test(      "Harper", "HARPAR"   );
        test(      "Jacobs", "JACAB"    );
        test(      "Larson", "LARSAN"   );
        test(    "Lawrence", "LARANC"   );
        test(      "Lawson", "LASAN"    );
        test(  "Louis, XVI", "LASXV"    );
        test(       "Lynch", "LYNC"     );
        test(   "Mackenzie", "MCANSY"   );
        test(    "Matthews", "MAT"      );
        test(   "McCormack", "MCARNAC"  );
        test(    "McDaniel", "MCDANAL"  );
        test(    "McDonald", "MCDANALD" );
        test(  "Mclaughlin", "MCLAGLAN" );
        test(    "Morrison", "MARASAN"  );
        test(    "O'Banion", "OBANAN"   );
        test(     "O'Brien", "OBRAN"    );
        test(    "Richards", "RACARD"   );
        test(       "Silva", "SALV"     );
        test(     "Watkins", "WATCAN"   );
        test(     "Wheeler", "WALAR"    );
        test(      "Willis", "WAL"      );
        test(   "brown, sr", "BRANSR"   );
        test( "browne, III", "BRAN"     );
        test(  "browne, IV", "BRANAV"   );
        test(       "hayes", "HAY"      );
        test(      "knight", "NAGT"     );
        test(    "mitchell", "MATCAL"   );
        test(    "o'daniel", "ODANAL"   );
    } // main
} // nysiisUsingApache
Output:
          Bishop -> BASAP
         Carlson -> CARLSAN
            Carr -> CAR
         Chapman -> CAPNAN
        Franklin -> FRANCLAN
          Greene -> GRAN
          Harper -> HARPAR
          Jacobs -> JACAB
          Larson -> LARSAN
        Lawrence -> LARANC
          Lawson -> LASAN
      Louis, XVI -> LASXV
           Lynch -> LYNC
       Mackenzie -> MCANSY
        Matthews -> MAT
       McCormack -> MCARNAC
        McDaniel -> MCDANAL
        McDonald -> MCDANALD
      Mclaughlin -> MCLAGLAN
        Morrison -> MARASAN
        O'Banion -> OBANAN
         O'Brien -> OBRAN
        Richards -> RACARD
           Silva -> SALV
         Watkins -> WATCAN
         Wheeler -> WALAR
          Willis -> WAL
       brown, sr -> BRANSR
     browne, III -> BRAN
      browne, IV -> BRANAV
           hayes -> HAY
          knight -> NAGT
        mitchell -> MATCAL
        o'daniel -> ODANAL

jq

This example is incorrect. Please fix the code and remove this message.

Details: Mathews should encode to MAT - see the discussion page

Adapted from Wren

Works with jq, the C implementation of jq

Works with gojq, the Go implementation of jq

Works with jaq, the Rust implementation of jq

### Generic utilities

def lpad($len): tostring | ($len - length) as $l | (" " * $l) + .;

# Output: a stream
def toChars: explode[] | [.] | implode;

### NYSIIS encoding

def fStrs: [["MAC", "MCC"], ["KN", "N"], ["K", "C"], ["PH", "FF"], ["PF", "FF"], ["SCH", "SSS"]];
def lStrs: [["EE", "Y"], ["IE", "Y"], ["DT", "D"], ["RT", "D"], ["RD", "D"], ["NT", "D"], ["ND", "D"]];
def mStrs: [["EV", "AF"], ["KN", "N"], ["SCH", "SSS"], ["PH", "FF"]];
def eStrs: ["JR", "JNR", "SR", "SNR"];

def isVowel: {"A": true, "E": true, "I": true, "O": true, "U": true}[.];

def isRoman:
  {"I": true, "V": true, "X": true} as $roman
  | all(explode[] | [.] | implode; $roman[.] );

def splitter: "[ |,]";

def nysiis:
  if . == "" then .
  else {w: ascii_upcase}
  | [.w | splits(splitter)] as $ww
  | if ($ww|length) > 1 and ($ww[-1]|isRoman) then .w = .w[0: (.w|length) - ($ww[-1]|length)] end
  | .w |= gsub("[ ,'-]"; "")
  | reduce eStrs[] as $eStr (.; 
        if (.w|endswith($eStr)) then .w |= .[0: length - ($eStr|length)] end)
  | reduce fStrs[] as $fStr (.; 
        if (.w|startswith($fStr[0])) then .w |= $fStr[1] + .[$fStr[0] | length :] end )
  | reduce lStrs[] as $lStr (.;
        if (.w|endswith($lStr[0])) then .w |= .[0:-2] + $lStr[1] end)
  | .key = .w[0:1]
  | .w |= .[1:]
  | reduce mStrs[] as $mStr (.; .w |= gsub($mStr[0]; $mStr[1]))
  | .sb = [.key[0:1], (.w|toChars)]
  | (.sb|length) as $len
  | reduce range(0; $len) as $i (.;
      .sb[$i] as $s
      | if   $s | test("[EIOU]") then .sb[$i] = "A"
        elif $s == "Q"           then .sb[$i] = "G"
        elif $s == "Z"           then .sb[$i] = "S"
        elif $s == "M"           then .sb[$i] = "N"
        elif $s == "K"           then .sb[$i] = "C"
        elif $s == "H"
        then
             if (.sb[$i-1] | isVowel | not) or ($i < $len-1 and ((.sb[$i+1] | isVowel) | not))
             then .sb[$i] = .sb[$i-1]
             end
        elif $s == "W"
        then if (.sb[$i-1]|isVowel) then .sb[$i] = "A" end
        end )
  | if .sb[-1] == "S" then .sb |= .[0: -1] end
  | if (.sb|length) > 1 and (.sb|join("")|.[-2:] == "AY")
    then .sb |= .[0:-2] + ["Y"]
    end
  | if .sb[-1] == "A" then .sb |= .[0:-1] end
  | .prev = .key[0:1]
  | reduce range(1; .sb|length) as $j (.;
      .sb[$j] as $c
      | if (.prev != $c)
        then .key += $c
        | .prev = $c
        end )
  | .key
  end ;

def names: [
    "Bishop", "Carlson", "Carr", "Chapman",
    "Franklin", "Greene", "Harper", "Jacobs", "Larson", "Lawrence",
    "Lawson", "Louis, XVI", "Lynch", "Mackenzie", "Matthews", "May jnr",
    "McCormack", "McDaniel", "McDonald", "Mclaughlin", "Morrison",
    "O'Banion", "O'Brien", "Richards", "Silva", "Watkins", "Xi",
    "Wheeler", "Willis", "brown, sr", "browne, III", "browne, IV",
    "knight", "mitchell", "o'daniel", "bevan", "evans", "D'Souza",
    "Hoyle-Johnson", "Vaughan Williams", "de Sousa", "de la Mare II"
];

names[]
| . as $name
| nysiis
| . as $ny
| (if length > 6 then "\($ny[0:6])(\($ny[6:]))"
   else $ny
   end) as $name2
| "\($name|lpad(16)) : \($name2)"
Output:
          Bishop : BASAP
         Carlson : CARLSA(N)
            Carr : CAR
         Chapman : CAPNAN
        Franklin : FRANCL(AN)
          Greene : GRAN
          Harper : HARPAR
          Jacobs : JACAB
          Larson : LARSAN
        Lawrence : LARANC
          Lawson : LASAN
      Louis, XVI : LA
           Lynch : LYNC
       Mackenzie : MCANSY
        Matthews : MATA
         May jnr : MY
       McCormack : MCARNA(C)
        McDaniel : MCDANA(L)
        McDonald : MCDANA(LD)
      Mclaughlin : MCLAGL(AN)
        Morrison : MARASA(N)
        O'Banion : OBANAN
         O'Brien : OBRAN
        Richards : RACARD
           Silva : SALV
         Watkins : WATCAN
              Xi : X
         Wheeler : WALAR
          Willis : WAL
       brown, sr : BRAN
     browne, III : BRAN
      browne, IV : BRAN
          knight : NAGT
        mitchell : MATCAL
        o'daniel : ODANAL
           bevan : BAFAN
           evans : EVAN
         D'Souza : DSAS
   Hoyle-Johnson : HAYLAJ(ANSAN)
Vaughan Williams : VAGANW(ALAN)
        de Sousa : DASAS
   de la Mare II : DALANA(R)

Julia

Works with: Julia version 1.0+
Translation of: Python
using Printf

function replaceat(text::AbstractString, position::Int, fromlist, tolist)
    for (f, t) in zip(fromlist, tolist)
        if startswith(text[position:end], f)
            return text[1:position-1] * t * text[position+length(f):end]
        end
    end
    return text
end

function replaceend(text::AbstractString, fromlist, tolist)
    for (f, t) in zip(fromlist, tolist)
        if endswith(text, f)
            return text[1:end-length(f)] * t
        end
    end
    return text
end

function nysiis(name::AbstractString)
    vowels = ["A", "E", "I", "O", "U"]

    name = uppercase(filter(isalpha, name))
    name = replaceat(name, 1, ["MAC", "KN", "K", "PH", "PF", "SCH"],
                               ["MCC", "N",  "C", "FF", "FF", "SSS"])
    name = replaceend(name, ["EE", "IE", "DT", "RT", "RD", "NT", "ND"],
                             ["Y",  "Y",  "D",  "D",  "D",  "D",  "D"])
    key, key1 = name[1:1], ""
    for i in 2:length(name)
        prev, curr = name[(i:i).-1], name[i:i]
        next = i < length(name) ? name[(i:i).+1] : ""
        name = replaceat(name, i, vcat("EV", vowels), ["AF", "A", "A", "A", "A", "A"])
        name = replaceat(name, i, "QZM", "GSN")
        name = replaceat(name, i, ["KN", "K"], ["N", "C"])
        name = replaceat(name, i, ["SCH", "PH"], ["SSS", "FF"])
        if curr == "H" && (prev  vowels || next  vowels)
            name = name[1:i-1] * prev * name[i+1:end]
        end
        if curr == "W" && prev  vowels
            name = name[1:i-1] * "A" * name[i+1:end]
        end
        if !isempty(key) && key[end:end] != name[i:i]
            key *= name[i:i]
        end
        i += 1
    end
    key = replaceend(key, ["S"],  [""])
    key = replaceend(key, ["AY"], ["Y"])
    key = replaceend(key, ["A"],  [""])
    return key1 * key
end

for name in ["Bishop", "Carlson", "Carr", "Chapman", "Franklin",
             "Greene", "Harper", "Jacobs", "Larson", "Lawrence",
             "Lawson", "Louis, XVI", "Lynch", "Mackenzie", "Matthews",
             "McCormack", "McDaniel", "McDonald", "Mclaughlin", "Morrison",
             "O'Banion", "O'Brien", "Richards", "Silva", "Watkins",
             "Wheeler", "Willis", "brown, sr", "browne, III", "browne, IV",
             "knight", "mitchell", "o'daniel"]
    @printf("%15s: %s\n", name, nysiis(name))
end
Output:
         Bishop: BASAP
        Carlson: CARLSAN
           Carr: CAR
        Chapman: CAPNAN
       Franklin: FRANCLAN
         Greene: GRAN
         Harper: HARPAR
         Jacobs: JACAB
         Larson: LARSAN
       Lawrence: LARANC
         Lawson: LASAN
     Louis, XVI: LASXV
          Lynch: LYNC
      Mackenzie: MCANSY
       Matthews: MAT
      McCormack: MCARNAC
       McDaniel: MCDANAL
       McDonald: MCDANALD
     Mclaughlin: MCLAGLAN
       Morrison: MARASAN
       O'Banion: OBANAN
        O'Brien: OBRAN
       Richards: RACARD
          Silva: SALV
        Watkins: WATCAN
        Wheeler: WALAR
         Willis: WAL
      brown, sr: BRANSR
    browne, III: BRAN
     browne, IV: BRANAV
         knight: NAGT
       mitchell: MATCAL
       o'daniel: ODANAL

Kotlin

// version 1.1.3

val fStrs = listOf("MAC" to "MCC", "KN" to "N", "K" to "C", "PH" to "FF",
                   "PF" to "FF", "SCH" to "SSS")

val lStrs = listOf("EE" to "Y", "IE" to "Y", "DT" to "D", "RT" to "D",
                   "RD" to "D", "NT" to "D", "ND" to "D")

val mStrs = listOf("EV" to "AF", "KN" to "N", "SCH" to "SSS", "PH" to "FF")

val eStrs = listOf("JR", "JNR", "SR", "SNR")

fun Char.isVowel() = this in "AEIOU"

fun String.isRoman() = this.all { it in "IVX" }
 
fun nysiis(word: String): String {
    if (word.isEmpty()) return word
    var w = word.toUpperCase()
    val ww = w.split(' ', ',')
    if (ww.size > 1 && ww.last().isRoman()) w = w.dropLast(ww.last().length)
    for (c in " ,'-") w = w.replace(c.toString(), "")
    for (eStr in eStrs) 
        if (w.endsWith(eStr)) w = w.dropLast(eStr.length)

    for (fStr in fStrs) 
        if (w.startsWith(fStr.first)) w = w.replaceFirst(fStr.first, fStr.second)
    
    for (lStr in lStrs) 
        if (w.endsWith(lStr.first)) w = w.dropLast(2) + lStr.second
    
    val key = StringBuilder().append(w[0])
    w = w.drop(1)
    for (mStr in mStrs) w = w.replace(mStr.first, mStr.second)    
    val sb = StringBuilder().append(key[0]).append(w)
    var i = 1
    var len = sb.length
    while (i < len) {
        when (sb[i]) {  
            in "EIOU" -> sb[i] = 'A'       
            'Q'       -> sb[i] = 'G'
            'Z'       -> sb[i] = 'S'
            'M'       -> sb[i] = 'N'
            'K'       -> sb[i] = 'C'
            'H'       -> if (!sb[i - 1].isVowel() || (i < len - 1 && !sb[i + 1].isVowel())) sb[i] = sb[i - 1]
            'W'       -> if (sb[i - 1].isVowel()) sb[i] = 'A' 
        }
        if (sb[i] != sb[i - 1]) {
            i++
        }
        else {
            sb.deleteCharAt(i)  // deprecated method - should use deleteAt in later Kotlin versions
            len--
        }
    }    
    if (sb[len - 1] == 'S') {
        sb.setLength(len - 1)
        len--
    }
    if (len > 1 && sb.substring(len - 2) == "AY") {
        sb.setLength(len - 2)
        sb.append("Y")
        len--
    }
    if (len > 0 && sb[len - 1] == 'A') {       
        sb.setLength(len - 1)
        len--
    }
    var prev = key[0]
    for (j in 1 until len) {
        val c = sb[j]
        if (prev != c) {
           key.append(c)
           prev = c
        }
    }
    return key.toString()
}

fun main(args:Array<String>) {
    val names = listOf(
        "Bishop", "Carlson", "Carr", "Chapman",
        "Franklin", "Greene", "Harper", "Jacobs", "Larson", "Lawrence",
        "Lawson", "Louis, XVI", "Lynch", "Mackenzie", "Matthews", "May jnr",
        "McCormack", "McDaniel", "McDonald", "Mclaughlin", "Morrison",
        "O'Banion", "O'Brien", "Richards", "Silva", "Watkins", "Xi",
        "Wheeler", "Willis", "brown, sr", "browne, III", "browne, IV",
        "knight", "mitchell", "o'daniel", "bevan", "evans", "D'Souza",
        "Hoyle-Johnson", "Vaughan Williams", "de Sousa", "de la Mare II"
    )
    for (name in names) { 
        var name2 = nysiis(name)
        if (name2.length > 6) name2 = "${name2.take(6)}(${name2.drop(6)})"
        println("${name.padEnd(16)} : $name2")
    }
}
Output:
Bishop           : BASAP
Carlson          : CARLSA(N)
Carr             : CAR
Chapman          : CAPNAN
Franklin         : FRANCL(AN)
Greene           : GRAN
Harper           : HARPAR
Jacobs           : JACAB
Larson           : LARSAN
Lawrence         : LARANC
Lawson           : LASAN
Louis, XVI       : L
Lynch            : LYNC
Mackenzie        : MCANSY
Matthews         : MAT
May jnr          : MY
McCormack        : MCARNA(C)
McDaniel         : MCDANA(L)
McDonald         : MCDANA(LD)
Mclaughlin       : MCLAGL(AN)
Morrison         : MARASA(N)
O'Banion         : OBANAN
O'Brien          : OBRAN
Richards         : RACARD
Silva            : SALV
Watkins          : WATCAN
Xi               : X
Wheeler          : WALAR
Willis           : WAL
brown, sr        : BRAN
browne, III      : BRAN
browne, IV       : BRAN
knight           : NAGT
mitchell         : MATCAL
o'daniel         : ODANAL
bevan            : BAFAN
evans            : EVAN
D'Souza          : DSAS
Hoyle-Johnson    : HAYLAJ(ANSAN)
Vaughan Williams : VAGANW(ALAN)
de Sousa         : DASAS
de la Mare II    : DALANA(R)

Nim

Translation of: Kotlin
import strutils

const
  FStrs = [("MAC", "MCC"), ("KN", "N"), ("K", "C"),
           ("PH", "FF"), ("PF", "FF"), ("SCH", "SSS")]
  LStrs = [("EE", "Y"), ("IE", "Y"), ("DT", "D"),
           ("RT", "D"), ("RD", "D"), ("NT", "D"), ("ND", "D")]
  MStrs = [("EV", "AF"), ("KN", "N"), ("SCH", "SSS"), ("PH", "FF")]
  EStrs = ["JR", "JNR", "SR", "SNR"]


func isVowel(c: char): bool = c in {'A', 'E', 'I', 'O', 'U'}

func isRoman(s: string): bool = s.allCharsInSet({'I', 'V', 'X'})


func nysiis(word: string): string =

  if word.len == 0: return
  var word  = word.toUpperAscii()
  let fields = word.split({' ', ','})
  if fields.len > 1:
    let last = fields[^1]
    if last.isRoman: word.setLen(word.len - last.len)
  word = word.multiReplace((" ", ""), (",", ""), ("'", ""), ("-", ""))
  for eStr in EStrs:
    if word.endsWith(eStr): word.setLen(word.len - eStr.len)
  for fStr in FStrs:
    if word.startsWith(fStr[0]): word[0..fStr[0].high] = fStr[1]
  for lStr in LStrs:
    if word.endsWith(lStr[0]): word[^2..^1] = lStr[1]

  result.add word[0]
  word.delete(0..0)
  for mStr in MStrs:
    word = word.replace(mStr[0], mStr[1])
  var s = result[0] & word
  var len = s.len
  for i in 1..<len:
    case s[i]
    of 'E', 'I', 'O', 'U': s[i] = 'A'
    of 'Q': s[i] = 'G'
    of 'Z': s[i] = 'S'
    of 'M': s[i] = 'N'
    of 'K': s[i] = 'C'
    of 'H': (if not s[i-1].isVowel or i < len - 1 and not s[i+1].isVowel: s[i] = s[i-1])
    of 'W': (if s[i-1].isVowel: s[i] = 'A')
    else: discard

  if s[len-1] == 'S':
    s.setLen(len-1)
    dec len
  if len > 1 and s[len-2..len-1] == "AY":
    s.delete(len-2..len-2)
    dec len
  if len > 0 and s[len-1] == 'A':
    s.setLen(len-1)
    dec len
  if len > 0 and s[len-1] == 'A':
    s.setLen(len-1)
    dec len

  var prev = result[0]
  for i in 1..<len:
    let c = s[i]
    if prev != c:
      result.add c
      prev = c


const Names = ["Bishop", "Carlson", "Carr", "Chapman",
               "Franklin", "Greene", "Harper", "Jacobs", "Larson", "Lawrence",
               "Lawson", "Louis, XVI", "Lynch", "Mackenzie", "Matthews", "May jnr",
               "McCormack", "McDaniel", "McDonald", "Mclaughlin", "Morrison",
               "O'Banion", "O'Brien", "Richards", "Silva", "Watkins", "Xi",
               "Wheeler", "Willis", "brown, sr", "browne, III", "browne, IV",
               "knight", "mitchell", "o'daniel", "bevan", "evans", "D'Souza",
               "Hoyle-Johnson", "Vaughan Williams", "de Sousa", "de la Mare II"]

for name1 in Names:
  var name2 = nysiis(name1)
  if name2.len > 6:
    name2 = "$1($2)".format(name2[0..5], name2[6..^1])
  echo name1.alignLeft(16), ": ", name2
Output:
Bishop          : BASAP
Carlson         : CARLSA(N)
Carr            : CAR
Chapman         : CAPNAN
Franklin        : FRANCL(AN)
Greene          : GRAN
Harper          : HARPAR
Jacobs          : JACAB
Larson          : LARSAN
Lawrence        : LARANC
Lawson          : LASAN
Louis, XVI      : LA
Lynch           : LYNC
Mackenzie       : MCANSY
Matthews        : MAT
May jnr         : MY
McCormack       : MCARNA(C)
McDaniel        : MCDANA(L)
McDonald        : MCDANA(LD)
Mclaughlin      : MCLAGL(AN)
Morrison        : MARASA(N)
O'Banion        : OBANAN
O'Brien         : OBRAN
Richards        : RACARD
Silva           : SALV
Watkins         : WATCAN
Xi              : X
Wheeler         : WALAR
Willis          : WAL
brown, sr       : BRAN
browne, III     : BRAN
browne, IV      : BRAN
knight          : NAGT
mitchell        : MATCAL
o'daniel        : ODANAL
bevan           : BAFAN
evans           : EVAN
D'Souza         : DSAS
Hoyle-Johnson   : HAYLAJ(ANSAN)
Vaughan Williams: VAGANW(ALAN)
de Sousa        : DASAS
de la Mare II   : DALANA(R)

Perl

Translation of: Raku
sub no_suffix {
    my($name) = @_;
    $name =~ s/\h([JS]R)|([IVX]+)$//i;
    return uc $name;
}

sub nysiis {
    my($name) = @_;
    local($_) = uc $name;

    s/[^A-Z]//g;
    s/^MAC/MCC/;
    s/^P[FH]/FF/;
    s/^SCH/SSS/;
    s/^KN/N/;
    s/[IE]E$/Y/;
    s/[DRN]T$/D/;
    s/[RN]D$/D/;
    s/(.)EV/$1AF/g;
    s/(.)[AEIOU]+/$1A/g;
    s/(.)Q/$1G/g;
    s/(.)Z/$1S/g;
    s/(.)M/$1N/g;
    s/(.)KN/$1N/g;
    s/(.)K/$1C/g;
    s/(.)SCH/$1S/g;
    s/(.)PF/$1F/g;
    s/(.)K/$1C/g;
    s/(.)H([^AEIOU])/$1$2/g;
    s/([^AEIOU])H/$1/g;
    s/(.)W/$1/g;
    s/AY$/Y/;
    s/S$//;
    s/A$//;
    s/(.)\1+/$1/g;
    return $_;
}

for (
    "knight",     "mitchell",  "o'daniel",    "brown sr",   "browne III",
    "browne IV",  "O'Banion",  "Mclaughlin",  "McCormack",  "Chapman",
    "Silva",      "McDonald",  "Lawson",      "Jacobs",     "Greene",
    "O'Brien",    "Morrison",  "Larson",      "Willis",     "Mackenzie",
    "Carr",       "Lawrence",  "Matthews",    "Richards",   "Bishop",
    "Franklin",   "McDaniel",  "Harper",      "Lynch",      "Watkins",
    "Carlson",    "Wheeler",   "Louis XVI"
) {
    my $nysiis = nysiis no_suffix $_;
    $nysiis =~ s/^(......)(.*)$/$1\[$2\]/ if length($nysiis) > 6;
    printf "%10s,  %s\n", $_, $nysiis;
}
Output:
    knight,  NAGT
  mitchell,  MATCAL
  o'daniel,  ODANAL
  brown sr,  BRAN
browne III,  BRAN
 browne IV,  BRAN
  O'Banion,  OBANAN
Mclaughlin,  MCLAGL[AN]
 McCormack,  MCARNA[C]
   Chapman,  CAPNAN
     Silva,  SALV
  McDonald,  MCDANA[LD]
    Lawson,  LASAN
    Jacobs,  JACAB
    Greene,  GRAN
   O'Brien,  OBRAN
  Morrison,  MARASA[N]
    Larson,  LARSAN
    Willis,  WAL
 Mackenzie,  MCANSY
      Carr,  CAR
  Lawrence,  LARANC
  Matthews,  MAT
  Richards,  RACARD
    Bishop,  BASAP
  Franklin,  FRANCL[AN]
  McDaniel,  MCDANA[L]
    Harper,  HARPAR
     Lynch,  LYNC
   Watkins,  WATCAN
   Carlson,  CARLSA[N]
   Wheeler,  WALAR
 Louis XVI,  L

Phix

Translation of: Go
with javascript_semantics
function isVowel(integer byte)
    return find(byte,"AEIOU")!=0
end function
 
function nysiis(string word)
    if word == "" then return "" end if
    word = upper(word)
    sequence ww = split_any(word, ", ")
    if length(ww)>1 then
        if length(filter(ww[$],"out","IVX"))=0 then
            word = word[1..-length(ww[$])-1]
        end if
    end if
    word = substitute_all(word, " ,'-", repeat("",4))
    for e in {"JR", "JNR", "SR", "SNR"} do
        if ends(e,word) then word=word[1..-length(e)-1] end if
    end for
    for f in {{"MAC","MCC"}, {"KN","N"}, {"K","C"},
              {"PH","FF"}, {"PF","FF"}, {"SCH","SSS"}} do
        if begins(f[1],word) then
            word[1..length(f[1])] = f[2]
        end if
    end for
    integer e = length(word)>=2 and find(word[-2..-1],
                {"EE","IE","DT","RT","RD","NT","ND"})
    if e then
        word[-2..-1] = iff(e<=2?"Y":"D")
    end if
    integer initial = word[1]
    string key = word[1..1]
    word = word[2..$]
    word = substitute_all(word,{"EV","KN","SCH","PH"},
                               {"AF","N", "SSS","FF"})
    string sb = key&word
    integer le := length(sb)
    for i=2 to le do
        switch sb[i] do
            case 'E', 'I', 'O', 'U':    sb[i] = 'A'
            case 'Q':                   sb[i] = 'G'
            case 'Z':                   sb[i] = 'S'
            case 'M':                   sb[i] = 'N'
            case 'K':                   sb[i] = 'C'
            case 'H':   if (i> 1 and not isVowel(sb[i-1]))
                        or (i<le and not isVowel(sb[i+1])) then
                            sb[i] = sb[i-1]
                        end if
            case 'W':   if isVowel(sb[i-1]) then
                            sb[i] = sb[i-1]
                        end if
        end switch
    end for
    integer prev := initial
    for j=2 to le do
        integer c := sb[j]
        if prev != c then
            key &= c
            prev = c
        end if
    end for
    if length(key)>=1 and key[$]      == 'S'  then  key[$  ..$] = ""    end if
    if length(key)>=2 and key[-2..-1] == "AY" then  key[$-1..$] = "Y"   end if
    if length(key)>=1 and key[$]      == 'A'  then  key[$  ..$] = ""    end if
    return key
end function
 
constant tests = {
                  { "Bishop", "BASAP" },
                  { "Carlson", "CARLSAN" },
                  { "Carr", "CAR" },
                  { "Chapman", "CAPNAN" },
                  { "Franklin", "FRANCLAN" },
                  { "Greene", "GRAN" },
                  { "Harper", "HARPAR" },
                  { "Jacobs", "JACAB" },
                  { "Larson", "LARSAN" },
                  { "Lawrence", "LARANC" },
                  { "Lawson", "LASAN" },
                  { "Louis, XVI", "L" },    -- (see note)
                  { "Lynch", "LYNC" },
                  { "Mackenzie", "MCANSY" },
                  { "Matthews", "MAT" },    -- (see note)
                  { "May jnr", "MY" },
                  { "McCormack", "MCARNAC" },
                  { "McDaniel", "MCDANAL" },
                  { "McDonald", "MCDANALD" },
                  { "Mclaughlin", "MCLAGLAN" },
                  { "Morrison", "MARASAN" },
                  { "O'Banion", "OBANAN" },
                  { "O'Brien", "OBRAN" },
                  { "Richards", "RACARD" },
                  { "Silva", "SALV" },
                  { "Watkins", "WATCAN" },
                  { "Wheeler", "WALAR" },
                  { "Willis", "WAL" },      -- (see note)
                  { "Xi", "X" },
                  { "bevan", "BAFAN" },
                  { "brown, sr", "BRAN" },
                  { "brown sr", "BRAN" },
                  { "browne, III", "BRAN" },
                  { "browne, IV", "BRAN" },
                  { "evans", "EVAN" },
                  { "knight", "NAGT" },
                  { "mitchell", "MATCAL" },
                  { "o'daniel", "ODANAL" },
                  { "D'Souza", "DSAS" },
                  { "de Sousa", "DASAS" },
                  { "Hoyle-Johnson", "HAYLAJANSAN" },
                  { "Vaughan Williams", "VAGANWALAN" },
                  { "de la Mare II", "DALANAR" } }
 
integer errors = 0
for i=1 to length(tests) do
    string {name,expected} = tests[i],
            actual := nysiis(name)
    if actual!=expected then
        errors += 1
        if length(actual) > 6 then
            actual = sprintf("%s(%s)", {actual[1..6], actual[7..$]})
        end if
        printf(1,"%-16s : %s\n", {name, actual})
    end if
end for
printf(1,"All tests completed, %d errors\n",errors)

Note: After some careful consideration, I have decided that all three (see note) tests are in fact correct, or at least follow wikipedia, specifically step 6 before step 7.

Output:
All tests completed, 0 errors

Python

A literal translation of the algorithm from the Wikipedia article.

import re

_vowels = 'AEIOU'

def replace_at(text, position, fromlist, tolist):
    for f, t in zip(fromlist, tolist):
        if text[position:].startswith(f):
            return ''.join([text[:position],
                            t,
                            text[position+len(f):]])
    return text

def replace_end(text, fromlist, tolist):
    for f, t in zip(fromlist, tolist):
        if text.endswith(f):
            return text[:-len(f)] + t
    return text

def nysiis(name):
    name = re.sub(r'\W', '', name).upper()
    name = replace_at(name, 0, ['MAC', 'KN', 'K', 'PH', 'PF', 'SCH'],
                               ['MCC', 'N',  'C', 'FF', 'FF', 'SSS'])
    name = replace_end(name, ['EE', 'IE', 'DT', 'RT', 'RD', 'NT', 'ND'],
                             ['Y',  'Y',  'D',  'D',  'D',  'D',  'D'])
    key, key1 = name[0], ''
    i = 1
    while i < len(name):
        #print(i, name, key1, key)
        n_1, n = name[i-1], name[i]
        n1_ = name[i+1] if i+1 < len(name) else ''
        name = replace_at(name, i, ['EV'] + list(_vowels), ['AF'] + ['A']*5)
        name = replace_at(name, i, 'QZM', 'GSN')
        name = replace_at(name, i, ['KN', 'K'], ['N', 'C'])
        name = replace_at(name, i, ['SCH', 'PH'], ['SSS', 'FF'])
        if n == 'H' and (n_1 not in _vowels or n1_ not in _vowels):
            name = ''.join([name[:i], n_1, name[i+1:]])
        if n == 'W' and n_1 in _vowels:
            name = ''.join([name[:i], 'A', name[i+1:]])
        if key and key[-1] != name[i]:
            key += name[i]
        i += 1
    key = replace_end(key, ['S'], [''])
    key = replace_end(key, ['AY'], ['Y'])
    key = replace_end(key, ['A'], [''])     
    return key1 + key

if __name__ == '__main__':
    names = ['Bishop', 'Carlson', 'Carr', 'Chapman', 'Franklin',
             'Greene', 'Harper', 'Jacobs', 'Larson', 'Lawrence',
             'Lawson', 'Louis, XVI', 'Lynch', 'Mackenzie', 'Matthews',
             'McCormack', 'McDaniel', 'McDonald', 'Mclaughlin', 'Morrison',
             "O'Banion", "O'Brien", 'Richards', 'Silva', 'Watkins',
             'Wheeler', 'Willis', 'brown, sr', 'browne, III', 'browne, IV',
             'knight', 'mitchell', "o'daniel"]
    for name in names:
        print('%15s: %s' % (name, nysiis(name)))
Output:
         Bishop: BASAP
        Carlson: CARLSAN
           Carr: CAR
        Chapman: CAPNAN
       Franklin: FRANCLAN
         Greene: GRAN
         Harper: HARPAR
         Jacobs: JACAB
         Larson: LARSAN
       Lawrence: LARANC
         Lawson: LASAN
     Louis, XVI: LASXV
          Lynch: LYNC
      Mackenzie: MCANSY
       Matthews: MAT
      McCormack: MCARNAC
       McDaniel: MCDANAL
       McDonald: MCDANALD
     Mclaughlin: MCLAGLAN
       Morrison: MARASAN
       O'Banion: OBANAN
        O'Brien: OBRAN
       Richards: RACARD
          Silva: SALV
        Watkins: WATCAN
        Wheeler: WALAR
         Willis: WAL
      brown, sr: BRANSR
    browne, III: BRAN
     browne, IV: BRANAV
         knight: NAGT
       mitchell: MATCAL
       o'daniel: ODANAL

Racket

This example is incorrect. Please fix the code and remove this message.

Details: Willis should encode to WAL and Mathews to MAT - see the discussion page

Translation of: Python

This is a translation of Python to ensure that these results are consistent. This allows them to be tested against a known output from another solution.

If any of the check-equal?s fails, it will print an error message. None of them fail, so no output is seen.

I’ve gone out of my way to number the rules — which may make it a little verbose.

#lang racket/base
(require racket/string racket/match)
(define (str-rplc-at str replacement start (end (string-length str)))
  (string-append (substring str 0 start) replacement (substring str end)))

(define (split-on-commas s) (string-split s "," #:trim? #f))

(define (str-rplc-at* fxt pos . more)
  (match more
    [(list (app split-on-commas froms) (app split-on-commas tos) even-more ...)
     (define txt-maybe
       (for/first ((f froms) (t tos) #:when (string-prefix? (substring fxt pos) f))
         (str-rplc-at fxt t pos (+ pos (string-length f)))))
     (apply str-rplc-at* (or txt-maybe fxt) pos even-more)]
    [_ fxt]))

(define (str-rplc-end* txf . more)
  (match more
    [(list (app split-on-commas froms) (app split-on-commas tos) even-more ...)
     (define txt-maybe
       (for/first ((f froms) (t tos) #:when (string-suffix? txf f))
         (str-rplc-at txf t (- (string-length txf) (string-length f)))))
     (apply str-rplc-end* (or txt-maybe txf) even-more)]
    [_ txf]))

(define vowels '("A" "E" "I" "O" "U"))
(define (vowel? s) (member s vowels))

;; ---------------------------------------------------------------------------------------------------
(define (normalise n) (regexp-replace* #px"\\W" (string-upcase n) ""))

(define (r:1 n) (str-rplc-at* n 0 "MAC,KN,K,PH,PF,SCH" "MCC,N,C,FF,FF,SSS"))

(define (r:2 n) (str-rplc-end* n "EE,IE,DT,RT,RD,NT,ND" "Y,Y,D,D,D,D,D"))

(define (r:3/4 in)
  (define (loop-4 name-4.1 key-3 i)
    (cond
      [(< i (string-length name-4.1))
       (define name-4.2/3/4
         (str-rplc-at* name-4.1 i "EV,A,E,I,O,U" "AF,A,A,A,A,A" #|4.1|# "Q,Z,M" "G,S,N" #|4.2|#
                       "KN,K" "N,C" #|4.3|# "SCH,PH" "SSS,FF" #|4.4|#))
       (define name-4.5/6
         (match-let ([(or (regexp "^(.)(.)(.)" (list n_1 n n1_))
                          (regexp "^(.)(.)" (list (app (λ _ "") n1_) n_1 n)))
                      (substring name-4.1 (sub1 i))])
           (match n
             ["H" #:when (or (not (vowel? n_1)) (not (vowel? n1_)))
                  (str-rplc-at name-4.2/3/4 n_1 i (add1 i))] ; 4.5
             ["W" #:when (vowel? n_1) (str-rplc-at name-4.2/3/4 "A" i (add1 i))] ; 4.6
             [_ name-4.2/3/4])))
       (define name-4.6_i (substring name-4.5/6 i (add1 i)))
       (define key-4.7 (if (string=? name-4.6_i (substring key-3 (sub1 (string-length key-3))))
                           key-3 (string-append key-3 name-4.6_i)))
       (loop-4 name-4.5/6 key-4.7 (add1 i))]
      [else key-3]))
  (loop-4 in (substring in 0 1) 1))

(define (r:5/6/7/8 n) (str-rplc-end* n "S,AY,A" ",Y,"))

(define r:9 (match-lambda [(regexp #px"^(.{6})(.+)" (list _ l r)) (format "~a[~a]" l r)] [n n]))

(define nysiis (apply compose (reverse (list normalise r:1 r:2 r:3/4 r:5/6/7/8 r:9))))

(module+ test
  (require rackunit)
  (define names
    (list "Bishop" "Carlson" "Carr" "Chapman" "Franklin" "Greene" "Harper" "Jacobs" "Larson"
          "Lawrence" "Lawson" "Louis, XVI" "Lynch" "Mackenzie" "Matthews" "McCormack" "McDaniel"
          "McDonald" "Mclaughlin" "Morrison" "O'Banion" "O'Brien" "Richards" "Silva" "Watkins"
          "Wheeler" "Willis" "brown, sr" "browne, III" "browne, IV" "knight" "mitchell" "o'daniel"))

  (define py-nysiis-names ; results from python (with [] added)
    (list "BASAP" "CARLSA[N]" "CAR" "CAPNAN" "FRANCL[AN]" "GRAN" "HARPAR" "JACAB" "LARSAN" "LARANC"
          "LASAN" "LASXV" "LYNC" "MCANSY" "MATA" "MCARNA[C]" "MCDANA[L]" "MCDANA[LD]" "MCLAGL[AN]"
          "MARASA[N]" "OBANAN" "OBRAN" "RACARD" "SALV" "WATCAN" "WALAR" "WALA" "BRANSR" "BRAN"
          "BRANAV" "NAGT" "MATCAL" "ODANAL"))

  (for ((n names) (p py-nysiis-names))
    (check-equal? (nysiis n) p (format "(nysiis ~s) = ~s" n p))))

Raku

(formerly Perl 6)

Works with: rakudo version 2017.05

This implementation removes common name suffixes similar to the reference implementation, even though it is not specified in the task description or on the linked NYSIIS page. This algorithm isn't too friendly to certain French kings. :)

sub no_suffix ($name) {
    $name.uc.subst: /\h (<[JS]>R) | (<[IVX]>+) $/, '';
}

sub nysiis ($name is copy) {
    given $name .= uc {
        s:g/<-[A..Z]>//;
        s/^MAC/MCC/;
        s/^P<[FH]>/FF/;
        s/^SCH/SSS/;
        s/^KN/N/;
        s/<[IE]>E$  /Y/;
        s/<[DRN]>T$ /D/;
        s/<[RN]>D$  /D/;
        s:c(1):g/EV/AF/;
        s:c(1):g/<[AEIOU]>+/A/;
        s:c(1):g/Q/G/;
        s:c(1):g/Z/S/;
        s:c(1):g/M/N/;
        s:c(1):g/KN/N/;
        s:c(1):g/K/C/;
        s:c(1):g/SCH/S/;
        s:c(1):g/PF/F/;
        s:c(1):g/K/C/;
        s:c(1):g/H(<-[AEIOU]>)/$0/;
        s:g/(<-[AEIOU]>)H/$0/;
        s:g/(.)W/$0/;
        s/ AY$ /Y/;
        s/  S$ //;
        s/  A$ //;
        s:g/ (.)$0+ /$0/;
     }
     return $name;
}


for «
    knight      mitchell        "o'daniel"      "brown  sr"     "browne III"
    "browne IV" "O'Banion"      Mclaughlin      McCormack       Chapman
    Silva       McDonald        Lawson          Jacobs          Greene
    "O'Brien"   Morrison        Larson          Willis          Mackenzie
    Carr        Lawrence        Matthews        Richards        Bishop
    Franklin    McDaniel        Harper          Lynch           Watkins
    Carlson     Wheeler         "Louis XVI"
» {
    my $nysiis = nysiis no_suffix $_;
    if $nysiis.chars > 6 {
        $nysiis .= subst: rx/ <after .**6> .+ /, -> $/ { "[$/]" };
    }
    printf "%10s,  %s\n", $_, $nysiis;
}

Output:

    knight,  NAGT
  mitchell,  MATCAL
  o'daniel,  ODANAL
  brown sr,  BRAN
browne III,  BRAN
 browne IV,  BRAN
  O'Banion,  OBANAN
Mclaughlin,  MCLAGL[AN]
 McCormack,  MCARNA[C]
   Chapman,  CAPNAN
     Silva,  SALV
  McDonald,  MCDANA[LD]
    Lawson,  LASAN
    Jacobs,  JACAB
    Greene,  GRAN
   O'Brien,  OBRAN
  Morrison,  MARASA[N]
    Larson,  LARSAN
    Willis,  WAL
 Mackenzie,  MCANSY
      Carr,  CAR
  Lawrence,  LARANC
  Matthews,  MAT
  Richards,  RACARD
    Bishop,  BASAP
  Franklin,  FRANCL[AN]
  McDaniel,  MCDANA[L]
    Harper,  HARPAR
     Lynch,  LYNC
   Watkins,  WATCAN
   Carlson,  CARLSA[N]
   Wheeler,  WALAR
 Louis XVI,  L

REXX

This REXX version allows a blank to be inserted into names by using an underscore or underbar character   [ _ ].

Code was added to the REXX program to allow various titles.

Any post-nominal letters (generational, honorific, professional,or other)   ending in a period is ignored as well as most Roman numeral letters.

If the rule of only returning (up to) six characters is to be enforced, then the last REXX statement should be replaced with:

return strip( left(key, 6) )                     /*return the leftmost six characters.  */
/*REXX program implements the  NYSIIS  phonetic algorithm  (for various test names).    */
@@= "Bishop brown_sr browne_III browne_IV Carlson Carr Chapman D'Souza de_Sousa Franklin",
    "Greene Harper Hoyle-Johnson Jacobs knight Larson Lawrence Lawson Louis_XVI Lynch",
    "Mackenzie Marshall,ESQ Matthews McCormack McDaniel McDonald Mclaughlin mitchell Morrison",
    "O'Banion O'Brien o'daniel Richards Silva Vaughan_Williams Watkins Wheeler Willis Xavier,MD."
parse upper arg z;     if z=''  then z= @@       /*obtain optional name list from the CL*/

        do i=1  for words(z)                     /*process each name (word) in the list.*/
        xx= translate( word(z, i), , '_')        /*reconstitute any blanks using TRANS. */
        say right(xx, 35)   ' ──► '   nysiis(xx) /*display some stuff to the terminal.  */
        end   /*i*/
exit 0                                           /*stick a fork in it,  we're all done. */
/*──────────────────────────────────────────────────────────────────────────────────────*/
$:     p= substr(x,j-1,1) /*prev*/; n= substr(x,j+1,1) /*next*/; return substr(x,j,arg(1))
vowel: return  pos(arg(1), 'AEIOUaeiou') \== 0   /*returns 1 if the argument has a vowel*/
/*──────────────────────────────────────────────────────────────────────────────────────*/
nysiis:  procedure; arg x;  x= space( translate(x, , ',')) /*elide commas, excess blanks*/
w= words(x);        Lw= word(x, w)               /*pick off the last word in name list. */
@titles= 'ESQ JNR JR SNR SR'                     /* [↓]  last word post─nominal letters?*/
if w\==1  then if pos('IL', lw)==0  then                       /*disallow IL as Roman #.*/
                          if pos(., x)\==0                |,   /*Sr.  Jr.  Esq.  ··· ?  */
                             datatype( left(Lw, 1), 'W')  |,   /*2nd  3rd  4th   ··· ?  */
                             verify(Lw, 'IVXL') ==0       |,   /*Roman numeral suffix?  */
                             wordpos(x, @titles)\==0  then x= subword(x, 1, w-1)
x= space(x, 0)                                   /*remove all whitespace from the name. */
if left(x, 3)=='MAC'                   then x= "MCC"substr(x, 4)     /*start with MAC ? */
if left(x, 2)=='KN'                    then x=   "N"substr(x, 3)     /*  "     "  KN  ? */
if left(x, 1)=='K'                     then x=   "C"substr(x, 2)     /*  "     "  K   ? */
if left(x, 2)=='PH' | left(x,2)=="PF"  then x=  'FF'substr(x, 3)     /*  "     "  PH,PF?*/
if left(x, 3)=='SCH'                   then x= "SSS"substr(x, 4)     /*  "     "  SCH ? */
r2= right(x, 2)
if wordpos(r2, 'EE IE')         \==0   then x= left(x, length(x)-2)"Y" /*ends with ··· ?*/
if wordpos(r2, 'DT RT RD NT ND')\==0   then x= left(x, length(x)-2)"D" /*  "    "   "  "*/
key= left(x, 1)                                                        /*use first char.*/

   do j=2  to length(x); if \datatype($(1), 'U')  then iterate /*¬ Latin letter? Skip it*/
   if $(2)=='EV'   then x= overlay("F", x, j+1)                /*have an  EV ?   Use F  */
                   else x= overlay( translate( $(1), 'AAAAGSN', "EIOUQZM"),  x, j)
   if $(2)=='KN'   then x= left(x, j-1)"N"substr(x, j+1)       /*have a   KN ?   Use N  */
                   else if $(1)=="K"  then x= overlay('C',x,j) /*  "  "   K  ?   Use C  */
   if $(3)=='SCH'  then x= overlay("SSS", x, j)                /*  "  "   SCH?   Use SSS*/
   if $(2)=='PH'   then x= overlay("FF",  x, j)                /*  "  "   PH ?   Use FF */
   if $(1)=='H'    then if \vowel(p) | \vowel(n)  then x= overlay( p , x, j)
   if $(1)=='W'    then if  vowel(p)              then x= overlay("A", x, j)
   if $(1)\== right(key, 1)                       then key= key || $(1) /*append to KEY.*/
   end   /*j*/
                                                                        /* [↓]  elide:  */
if right(key, 1)=='S'   then key= left(key, max(1, length(key) -1))     /*ending S      */
if right(key, 2)=='AY'  then key= left(key,        length(key) -2)"Y"   /*  "    A in AY*/
if right(key, 1)=='A'   then key= left(key, max(1, length(key) -1))     /*  "    A      */
return strip(key)                                /*return the whole key  (all of it).   */
output   when using the default input:
                             Bishop  ──►  BASAP
                           brown sr  ──►  BRANSR
                         browne III  ──►  BRAN
                          browne IV  ──►  BRAN
                            Carlson  ──►  CARLSAN
                               Carr  ──►  CAR
                            Chapman  ──►  CAPNAN
                            D'Souza  ──►  DSAS
                           de Sousa  ──►  DASAS
                           Franklin  ──►  FRANCLAN
                             Greene  ──►  GRAN
                             Harper  ──►  HARPAR
                      Hoyle-Johnson  ──►  HAYLAJANSAN
                             Jacobs  ──►  JACAB
                             knight  ──►  NAGT
                             Larson  ──►  LARSAN
                           Lawrence  ──►  LARANC
                             Lawson  ──►  LASAN
                          Louis XVI  ──►  L
                              Lynch  ──►  LYNC
                          Mackenzie  ──►  MCANSY
                       Marshall,ESQ  ──►  MARSALASG
                           Matthews  ──►  MAT
                          McCormack  ──►  MCARNAC
                           McDaniel  ──►  MCDANAL
                           McDonald  ──►  MCDANALD
                         Mclaughlin  ──►  MCLAGLAN
                           mitchell  ──►  MATCAL
                           Morrison  ──►  MARASAN
                           O'Banion  ──►  OBANAN
                            O'Brien  ──►  OBRAN
                           o'daniel  ──►  ODANAL
                           Richards  ──►  RACARD
                              Silva  ──►  SALV
                   Vaughan Williams  ──►  VAGANWALAN
                            Watkins  ──►  WATCAN
                            Wheeler  ──►  WALAR
                             Willis  ──►  WAL
                         Xavier,MD.  ──►  XAVAR

Tcl

proc nysiis {name {truncate false}} {
    # Normalize to first word, uppercased, without non-letters
    set name [regsub -all {[^A-Z]+} [string toupper [regexp -inline {\S+} $name]] ""]
    # Prefix map
    foreach {from to} {MAC MCC KN N K C PH FF PF FF SCH SSS} {
	if {[regsub ^$from $name $to name]} break
    }
    # Suffix map
    foreach {from to} {EE Y IE Y DT D RT D NT D ND D} {
	if {[regsub $from$ $name $to name]} break
    }
    # Split
    regexp (.)(.*) $name -> name rest
    # Reduce suffix
    regsub -all {[AEIOU]} [regsub -all EV $rest AF] A rest
    set rest [string map {Q G Z S M N KN N K C SCH SSS PH FF} $rest]
    regsub -all {([^A])H|(.)H(?=[^A])} $rest {\1\2} rest
    regsub -all AW $rest A rest
    regsub -all {(.)\1+} $rest {\1} rest
    regsub {S$} $rest "" rest
    regsub {A(Y?)$} $rest {\1} rest
    append name $rest
    # Apply truncation if needed
    if {$truncate} {
	set name [string range $name 0 5]
    }
    return $name
}

Demonstrating:

foreach name {
    knight      mitchell        "o'daniel"      "brown  sr"     "browne III"
    "browne IV" "O'Banion"      Mclaughlin      McCormack       Chapman
    Silva       McDonald        Lawson          Jacobs          Greene
    "O'Brien"   Morrison        Larson          Willis          Mackenzie
    Carr        Lawrence        Matthews        Richards        Bishop
    Franklin    McDaniel        Harper          Lynch           Watkins
    Carlson     Wheeler         "Louis XVI"
} {
    puts "$name -> [nysiis $name]"
}
Output:
knight -> NAGT
mitchell -> MATCAL
o'daniel -> ODANAL
brown  sr -> BRAN
browne III -> BRAN
browne IV -> BRAN
O'Banion -> OBANAN
Mclaughlin -> MCLAGLAN
McCormack -> MCARNAC
Chapman -> CHAPNAN
Silva -> SALV
McDonald -> MCDANALD
Lawson -> LASAN
Jacobs -> JACAB
Greene -> GRAN
O'Brien -> OBRAN
Morrison -> MARASAN
Larson -> LARSAN
Willis -> WAL
Mackenzie -> MCANSY
Carr -> CAR
Lawrence -> LARANC
Matthews -> MAT
Richards -> RACARD
Bishop -> BASAP
Franklin -> FRANCLAN
McDaniel -> MCDANAL
Harper -> HARPAR
Lynch -> LYNC
Watkins -> WATCAN
Carlson -> CARLSAN
Wheeler -> WHALAR
Louis XVI -> L

Wren

Translation of: Kotlin
Library: Wren-str
Library: Wren-pattern
Library: Wren-fmt
import "./str" for Str
import "./pattern" for Pattern
import "./fmt" for Fmt

var fStrs = [["MAC", "MCC"], ["KN", "N"], ["K", "C"], ["PH", "FF"], ["PF", "FF"], ["SCH", "SSS"]]
var lStrs = [["EE", "Y"], ["IE", "Y"], ["DT", "D"], ["RT", "D"], ["RD", "D"], ["NT", "D"], ["ND", "D"]]
var mStrs = [["EV", "AF"], ["KN", "N"], ["SCH", "SSS"], ["PH", "FF"]]
var eStrs = ["JR", "JNR", "SR", "SNR"]

var isVowel = Fn.new { |c| "AEIOU".contains(c) }

var isRoman = Fn.new { |s| s.all { |c| "IVX".contains(c) } }

var splitter = Pattern.new("[ |,]")

var nysiis = Fn.new { |word|
    if (word == "") return word
    var w = Str.upper(word)
    var ww = splitter.splitAll(w)
    if (ww.count > 1 && isRoman.call(ww[-1])) w = w[0...w.count-ww[-1].count]
    for (c in " ,'-") w = w.replace(c, "")
    for (eStr in eStrs) {
        if (w.endsWith(eStr)) w = w[0...w.count-eStr.count]
    }
    for (fStr in fStrs) {
        if (w.startsWith(fStr[0])) w = fStr[1] + w[fStr[0].count..-1]
    }
    for (lStr in lStrs) {
        if (w.endsWith(lStr[0])) w = w[0..-3] + lStr[1]
    }
    var key = w[0]
    w = w[1..-1]
    for (mStr in mStrs) w = w.replace(mStr[0], mStr[1])
    var sb = (key[0] + w).toList
    var i = 1
    var len = sb.count
    while (i < len) {
        if ("EIOU".contains(sb[i])) {
            sb[i] = "A"
        } else if (sb[i] == "Q") {
            sb[i] = "G"
        } else if (sb[i] == "Z") {
            sb[i] = "S"
        } else if (sb[i] == "M") {
            sb[i] = "N"
        } else if (sb[i] == "K") {
            sb[i] = "C"
        } else if (sb[i] == "H") {
            if (!isVowel.call(sb[i-1]) || (i < len-1 && !isVowel.call(sb[i+1]))) sb[i] = sb[i-1]
        } else if (sb[i] == "W") {
            if (isVowel.call(sb[i-1])) sb[i] = "A"
        }
        if (sb[i] != sb[i-1]) {
            i = i + 1
        } else {
            sb.removeAt(i)
            len = len - 1
        }        
    }
    if (sb[len-1] == "S") {
        sb = sb[0...len-1]
        len = len - 1
    }
    if (len > 1 && Str.sub(sb.join(""), len-2..-1) == "AY") {
        sb = sb[0...len-2]
        sb = sb + ["Y"]
        len = len -1
    }
    if (len > 0 && sb[len-1] == "A") {
        sb = sb[0...len-1]
        len = len - 1
    }
    var prev = key[0]
    for (j in 1...len) {
        var c = sb[j]
        if (prev != c) {
            key = key + c
            prev = c
        }
    }
    return key
}

var names = [
    "Bishop", "Carlson", "Carr", "Chapman",
    "Franklin", "Greene", "Harper", "Jacobs", "Larson", "Lawrence",
    "Lawson", "Louis, XVI", "Lynch", "Mackenzie", "Matthews", "May jnr",
    "McCormack", "McDaniel", "McDonald", "Mclaughlin", "Morrison",
    "O'Banion", "O'Brien", "Richards", "Silva", "Watkins", "Xi",
    "Wheeler", "Willis", "brown, sr", "browne, III", "browne, IV",
    "knight", "mitchell", "o'daniel", "bevan", "evans", "D'Souza",
    "Hoyle-Johnson", "Vaughan Williams", "de Sousa", "de la Mare II"
]

for (name in names) {
    var name2 = nysiis.call(name)
    if (name2.count > 6) name2 = Fmt.swrite("$s($s)", name2[0..5], name2[6..-1])
    Fmt.print("$-16s : $s", name, name2)
}
Output:
Bishop           : BASAP
Carlson          : CARLSA(N)
Carr             : CAR
Chapman          : CAPNAN
Franklin         : FRANCL(AN)
Greene           : GRAN
Harper           : HARPAR
Jacobs           : JACAB
Larson           : LARSAN
Lawrence         : LARANC
Lawson           : LASAN
Louis, XVI       : L
Lynch            : LYNC
Mackenzie        : MCANSY
Matthews         : MAT
May jnr          : MY
McCormack        : MCARNA(C)
McDaniel         : MCDANA(L)
McDonald         : MCDANA(LD)
Mclaughlin       : MCLAGL(AN)
Morrison         : MARASA(N)
O'Banion         : OBANAN
O'Brien          : OBRAN
Richards         : RACARD
Silva            : SALV
Watkins          : WATCAN
Xi               : X
Wheeler          : WALAR
Willis           : WAL
brown, sr        : BRAN
browne, III      : BRAN
browne, IV       : BRAN
knight           : NAGT
mitchell         : MATCAL
o'daniel         : ODANAL
bevan            : BAFAN
evans            : EVAN
D'Souza          : DSAS
Hoyle-Johnson    : HAYLAJ(ANSAN)
Vaughan Williams : VAGANW(ALAN)
de Sousa         : DASAS
de la Mare II    : DALANA(R)

zkl

Translation of: Python
fcn replaceAt(text,pos,fromList,toList){
   foreach f,t in (fromList.zip(toList)){
      if(0==text[pos,*].find(f)) return(text.set(pos,f.len(),t));
   }
   text
}
fcn replaceEnd(text,fromList,toList){
   foreach f,t in (fromList.zip(toList)){
      if ((text.len() - f.len())==text.rfind(f)) return(text.set(-f.len(),*,t));
   }
   text
}
 
fcn nysiis(name){
   vowels := "AEIOU";
//   name = name.filter(fcn(c){ (not c.isSpace()) }).toUpper();
   name = name.toUpper().filter("matches","[A-Z]");
   name = replaceAt(name,0, T("MAC", "KN", "K", "PH", "PF", "SCH"),
                            T("MCC", "N",  "C", "FF", "FF", "SSS"));
   name = replaceEnd(name,T("EE", "IE", "DT", "RT", "RD", "NT", "ND"),
                          T("Y",  "Y",  "D",  "D",  "D",  "D",  "D"));
   key := name[0];
   foreach i in ([1 .. name.len()-1]){
      n_1,n,n1b:= name[i-1], name[i], name[i+1,1]; // "" if i+1>len
      name  = replaceAt(name,i, T("EV").extend(vowels.split("")), 
                                T("AF","A","A","A","A","A"));
      name  = replaceAt(name,i, T("Q","Z","M"), T("G","S","N"));
      name  = replaceAt(name,i, T("KN", "K"), T("N", "C"));
      name  = replaceAt(name,i, T("SCH", "PH"), T("SSS", "FF"));
      if (n=="H" and (not vowels.holds(n_1) or not vowels.holds(n1b)))
            name = name.set(i,1,n_1);
      if (n=="W" and vowels.holds(n_1)) name = name.set(i,1,"A");
      if (key[-1,1] != name[i]) key += name[i];
   }
   replaceEnd(replaceEnd(replaceEnd(key, T("S"), T("")), T("AY"), T("Y")), T("A"), T(""));
}
names := T("Bishop", "Carlson", "Carr", "Chapman",
        "Franklin", "Greene", "Harper", "Jacobs", "Larson", "Lawrence",
        "Lawson", "Louis, XVI", "Lynch", "Mackenzie", "Matthews",
         "McCormack", "McDaniel", "McDonald", "Mclaughlin", "Morrison",
         "O'Banion", "O'Brien", "Richards", "Silva", "Watkins",
         "Wheeler", "Willis", "brown, sr", "browne, III", "browne, IV",
         "knight", "mitchell", "o'daniel");
 
foreach name in (names){ println("%11s: %s".fmt(name, nysiis(name))) }
Output:
     Bishop: BASAP
    Carlson: CARLSAN
       Carr: CAR
    Chapman: CAPNAN
   Franklin: FRANCLAN
     Greene: GRAN
     Harper: HARPAR
     Jacobs: JACAB
     Larson: LARSAN
   Lawrence: LARANC
     Lawson: LASAN
 Louis, XVI: LASXV
      Lynch: LYNC
  Mackenzie: MCANSY
   Matthews: MAT
  McCormack: MCARNAC
   McDaniel: MCDANAL
   McDonald: MCDANALD
 Mclaughlin: MCLAGLAN
   Morrison: MARASAN
   O'Banion: OBANAN
    O'Brien: OBRAN
   Richards: RACARD
      Silva: SALV
    Watkins: WATCAN
    Wheeler: WALAR
     Willis: WAL
  brown, sr: BRANSR
browne, III: BRAN
 browne, IV: BRANAV
     knight: NAGT
   mitchell: MATCAL
   o'daniel: ODANAL