Implementando um Extension Method para IEnumerables utilizarem a expressão Soundex

Standard

Imagem 1

 

Vou demostrar neste artigo como podemos criar um Extension Method para coleções (IEnumerable) poderem utilizar um recurso muito utilizado em Transact-SQL chamado Soundex.

Para quem não conhece [ou não sabe nem o que é], a expressão Soundex retorna um código de quatro caracteres utilizado para comparar a semelhança entre duas sequências de caracteres. Ou seja, foneticamente falando, “casa” seria o mesmo que “caza”, ou até mesmo “cazza”, pois o som ao reproduzi-las seria o mesmo.

O algoritmo para a geração dos quatro caracteres para a comparação fonética é realizado utilizando algumas regras e “quase todas” letras do alfabeto. Onde básicamente, as seguintes regras são consideradas:

 

1 – Guarda-se o primeiro caracter da sequência e ignora os demais que forem iguais a (a, e, i, o, u, y, h, w);

2 – concatena-se os próximos caracteres com seu respectivo código:

  • b, f, p, v => 1
  • c, g, j, k, q, s, x, z => 2
  • d, t => 3
  • l => 4
  • m, n => 5
  • r => 6

3 – Dois caracteres adjacentes com o mesmo número são codificados como um único número, assim como, dois caracteres iguais separados por ‘h’ ou ‘w’ são codificados como um número único.

4 – Se houver letras duplicadas, a segunda letra é ignorada

5  – Continue até que o resultado seja três números, ou, preencher com “0” até ficarem três números.

Veja aqui mais informações sobre a expressão Soundex.

Vamos criar nossa rotina para geração do código Soundex:

/// <summary>
/// Gera código Soundex
/// </summary>
/// <param name="valor">Valor base a ser verificado</param>
/// <returns>String com código Soundex</returns>
public static string GeraSoundex(this string valor)
{
    if (String.IsNullOrEmpty(valor))
        return null;

    StringBuilder resultado = new StringBuilder(valor.Length);

    string valorBase = valor.ToUpper().Replace(" ", string.Empty);

    resultado.Append(valorBase[0]);
    char valorAnterior = '0';
    for (int i = 1; i < valorBase.Length; i++)
    {
        char valorEquivalente = '0';
        char valorAtual = valorBase[i];

        if ("BFPV".Contains(valorAtual))
            valorEquivalente = '1';

        else if ("CGJKQSXZ".Contains(valorAtual))
            valorEquivalente = '2';

        else if ("DT".Contains(valorAtual))
            valorEquivalente = '3';

        else if ('L'.Equals(valorAtual))
            valorEquivalente = '4';

        else if ("MN".Contains(valorAtual))
            valorEquivalente = '5';

        else if ('R'.Equals(valorAtual))
            valorEquivalente = '6';

        if (valorEquivalente != valorAnterior && valorEquivalente != '0')
        {
            resultado.Append(valorEquivalente);
            valorAnterior = valorEquivalente;
        }
    }
    while (resultado.Length < 4)
        resultado.Append("0");

    return resultado.ToString(0, 4);
}

Agora vamos criar a rotina para realizar a comparação dos valores, veja:

/// <summary>
/// Efetua a comparação entre as strings
/// </summary>
/// <param name="valor">Valor base da comparação</param>
/// <param name="valorComparar">Valor a ser comparado</param>
/// <returns>Bool se fonéticamente as strings são iguais</returns>
public static bool ComparaCodigoFonetico(this string valor, string valorComparar)
{
    int resultado1, resultado2 = 0;

    string valor1 = valor.GeraSoundex();
    string valor2 = valorComparar.GeraSoundex();

    if (!String.IsNullOrEmpty(valor1))
        resultado1 = Convert.ToInt32(valor1[0]) * 1000 + Convert.ToInt32(valor1.Substring(1, 3));

    if (!String.IsNullOrEmpty(valor2))
        resultado2 = Convert.ToInt32(valor2[0]) * 1000 + Convert.ToInt32(valor2.Substring(1, 3));

    return valor1.Equals(valor2);
}

Até o momento nossa implementação já pode ser utilizada entre duas strings:

bool equals = "casa".ComparaCodigoFonetico("caza");

Porém, seria interessante poder utiliza-lo como filters para coleções, veja:

/// <summary>
/// Realiza a comparação fonética das palavras (SOUNDEX)
/// </summary>
/// <param name="source">IEnumerable base</param>
/// <param name="propriedadeComparar">Propriedade string a ser comparada</param>
/// <param name="valorComparar">Valor string a ser comparado</param>
/// <returns>IEnumerable</returns>
public static IEnumerable<TSource> Soundex<TSource>(this IEnumerable<TSource> source, Func<TSource, string> propriedadeComparar, string valorComparar)
{
    return from uSource in source
            where propriedadeComparar(uSource).ComparaCodigoFonetico(valorComparar)
            select uSource;
}

Agora podemos facilmente realizar consultas Soundex em coleções base:

var colecao = _db.Imovels.Soundex(p => p.Nome, "caza");

Legal não acham?
Por hoje era isso Cool

 

Abraços.

 

Leave a Reply

Your email address will not be published. Required fields are marked *