JSFiddle - React, Tailwind, and code Playground

by mgibsonbr

HTML

<ul>
</ul>
<input />
<button>buscar</button>
<button>arvore</button>
<button>\xe1rvore</button>
<button>a\u0301rvore</button>
<br/>
<label><input type="checkbox" checked />Ignorar maiúsculas/minúsculas</label>
<label title="Se checado, acentos no termo de busca casam com palavras não acentuadas também. Se deixado sem, acentos no termo de busca só casam com letras igualmente acentuadas (embora as mesmas podem estar representadas de uma forma diferente)."><input type="checkbox" checked />Ignorar acento no termo de busca</label>
<hr/>
Tempo de preparo: <span>&#x221e;</span> µs (só faz sentido se a lista for grande)
<br/>
Tempo médio de busca: <span>&#x221e;</span> µs (<span>0</span> casos)

CSS

.achou {
    background: lightgray;
}

JavaScript

function preparar(lista) {
    return lista;
}

function prepararTermoBusca(value, ignoreCase, ignoreAcento) {
    var chars = ['aáàãäâ', 'eéèëê', 'iíìïî', 'oóòõöô', 'uúùüû', 'cç', 'nñ',
                 'AÁÀÃÄÂ', 'EÉÈËÊ', 'IÍÌÏÎ', 'OÓÒÕÖÔ', 'UÚÙÜÛ', 'CÇ', 'NÑ'
                ];
    
    function qual(i) {
        return ignoreAcento ? chars[i] : chars[i][0];
    }
    
    for (var i = 0 ; i < chars.length ; i++)
        value = value.replace(new RegExp('[' + qual(i) + ']', 'g'), '[' + chars[i] + ']');
    if ( ignoreCase )
        for ( var i = 0 ; i < chars.length ; i++ )
            value = value.replace('[' + chars[i] + ']', '[' + chars[i] + chars[i+7%14] + ']');
    return new RegExp(value, ignoreCase ? 'i' : '');
}

function comparar(elementoLista, termoBusca, ignoreCase) {
    return elementoLista.search(termoBusca) > -1;
}

////// Teste //////

var lista = [
    "acentuacao",
    'arvore',
    '\xe1rvore',
    'a\u0301rvore',
    "divagacão",
    "programaçao",
    "taxação"
];
var listaPreparada = null;

for ( var i = 0 ; i < lista.length ; i++ )
    $("ul").append('<li>' + lista[i] + '</li>');

var buscas = [];

$("button:eq(0)").click(function() {
    var termoBusca = $("input:eq(0)").val();
    $("li").removeClass("achou");
    
    if ( !listaPreparada ) {
        var now = new Date();
        var repetir = 1000;
        while ( repetir-- > 0 )
            listaPreparada = preparar(lista);
        $("span:eq(0)").text(((new Date() - now)).toFixed(3));
    }
    
    var ignoreCase = $("input:eq(1)").prop("checked");
    var ignoreAcento = $("input:eq(2)").prop("checked");
    
    var now = new Date();
    var repetir = 1000;
    while ( repetir-- > 0 ) {
        var termoPreparado = prepararTermoBusca(termoBusca, ignoreCase, ignoreAcento);
        for ( var i = 0 ; i < listaPreparada.length ; i++ )
            if ( comparar(listaPreparada[i], termoPreparado) )
                $("li").eq(i).addClass("achou");
    }
    buscas.push((new Date() -...