Soundex

func soundex(word, length=3) {

    # Uppercase the argument passed in to normalize it
    # and drop any non-alphabetic characters
    word.uc!.tr!('A-Z', '', 'cd')

    # Return if word does not contain 'A-Z'
    return(nil) if (word.is_empty)

    var firstLetter = word.char(0)

    # Replace letters with corresponding number values
    word.tr!('BFPV',     '1', 's')
    word.tr!('CGJKQSXZ', '2', 's')
    word.tr!('DT',       '3', 's')
    word.tr!('L',        '4', 's')
    word.tr!('MN',       '5', 's')
    word.tr!('R',        '6', 's')

    # Discard the first letter
    word.ft!(1)

    # Remove A, E, H, I, O, U, W, and Y
    word.tr!('AEHIOUWY', '', 'd')

    # Return the soundex code
    firstLetter + (word.chars + length.of('0') -> first(length).join)
}

func testSoundex {

    # Key-value pairs of names and corresponding Soundex codes
    var sndx = Hash(
                "Euler"                => "E4600",
                "Gauss"                => "G2000",
                "Hilbert"              => "H4163",
                "Knuth"                => "K5300",
                "Lloyd"                => "L3000",
                "Lukasieicz"           => "L2220",
                'fulkerson'            => 'F4262',
                'faulkersuhn'          => 'F4262',
                'fpfffffauhlkkersssin' => 'F4262',
                'Aaeh'                 => 'A0000',
               )

    sndx.keys.sort.each { |name|
        var findSdx = soundex(name, 4)
        say "The soundex for #{name} should be #{sndx{name}} and is #{findSdx}"
        if (findSdx != sndx{name}) {
            say "\tHowever, that is incorrect!\n"
        }
    }
}

testSoundex()