1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
use unicode_normalization::UnicodeNormalization;

pub fn soundex(s: &str) -> String {
    if s.len() == 0 {
        return String::from("");
    }

    let v = &s.to_uppercase().nfkd().collect::<Vec<char>>();

    let mut result = Vec::new();
    result.push(v[0]);

    let replacement = |ch| match ch {
        'B' | 'F' | 'P' | 'V' => '1',
        'C' | 'G' | 'J' | 'K' | 'Q' | 'S' | 'X' | 'Z' => '2',
        'D' | 'T' => '3',
        'L' => '4',
        'M' | 'N' => '5',
        'R' => '6',
        _ => '*',
    };

    // find would be replacement for first character
    let mut last = replacement(v[0]);

    // loop over remaining letters
    for letter in v.iter().skip(1) {
        let sub = replacement(*letter);
        if sub != '*' {
            if sub != last {
                result.push(sub);
                if result.len() == 4 {
                    break;
                }
            }
            last = sub;
        } else if *letter != 'H' && *letter != 'W' {
            last = '*';
        }
    }

    while result.len() < 4 {
        result.push('0');
    }
    let mut str_key = String::new();
    for k in result {
        str_key.push(k);
    }
    return str_key;
}

#[cfg(test)]
mod test {
    use super::*;
    use crate::testutils::testutils;
    #[test]
    fn test_soundex() {
        testutils::test_str_func("testdata/soundex.csv", soundex);
    }
}