martes, julio 01, 2014

AngularJS Case & accent insensitive filter

Siguiendo con AngularJS, requería de un filtro que me permitiera rescatar objetos desde un arreglo, pero con la particularidad de ignorar capitalización y caracteres especiales. Como no encontré nada terminé haciendo mi propio filtro, que comparto con los honorables lectores.

Básicamente la necesidad surgió ya que revisando un proyecto que está casi listo para su entrega final, nos percatamos que en uno de los infinitos procesos de re-creación de base de datos (que la bajaban y la volvían a crear), los datos de algunos conjuntos de combos de selección relacionados entre si (como puede ser país, región, estado, provincia, condado) no eran consistentes.

En este caso (sumamente particular), la relación (lo que podríamos decir "llave foránea") es un descriptor en texto, y el comité creativo por algún misterioso motivo puso en algunos combos datos en mayúsculas con acentos, en otros en minúsculas y todas las variaciones que se puedan imaginar.

El resultado de esto fue que el proceso de filtro que cargaba "el siguiente" combo relacionado, traía todos los datos, ignorando cualquier relación esperable.
Para evitar esto, y suponiendo posible inconsistencia en los datos, se debía construir un filtro personalizado que hiciera que cadenas de caracteres como "REPUBLICA     DE MÉXICO" y "República de México" fueran comparables, y que el resultado fuera true.

Para ello lo más sano era eliminar acentos y espacios, pasar todo a mayúsculas y comparar.

El código:


app.filter('cleancompare', function(){
    return function(items, filterObj){
        if (filterObj === false) {
          return items;
        }
        
        if ((filterObj || angular.isUndefined(filterObj)) && angular.isArray(items)) {
            var newItems = [];
            angular.forEach(items, function (item) {
                var matches = true;                             
                var kArr = Object.keys(filterObj);
                for(key in kArr){                    
                    var cleanItemAttr = removeDiacritics(item[kArr[key]]).replace(/[^\w]/gi, '').toUpperCase();
                    var cleanObjAttr  = removeDiacritics(filterObj[kArr[key]]).replace(/[^\w]/gi, '').toUpperCase();
                    matches = (cleanItemAttr != cleanObjAttr)?false:matches;  
                    // Descomentar la línea únicamente si es estrictamente necesario depurar,
                    // ya que el rendimiento se ve seriamente perjudicado para conjuntos muy 
                    // grandes de datos.
                    /*if(matches){ console.log('Comparacion realizada: ', cleanItemAttr, cleanObjAttr); }*/
                }
                if(matches){
                    newItems.push(item);
                }
            });
            
            items = newItems;
        }
        
        return items;
    }
});
 
La función removeDiacritics() la saqué de http://stackoverflow.com/questions/18123501/replacing-accented-characters-with-plain-ascii-ones
Disfruten ;)