Introducción

Hola, bienvenidos al post donde veremos como trabajar con la librería de node Fuzzy en Vue Js. Esta librería nos permite integrar un buscador en nuestra página web en cuestión de minutos.

Captura

Tendréis disponibles al final del post el código de GitHub con varios ejemplos para que veáis el potencial y el funcionamiento de este buscador, además del repositorio del autor de la librería.

Requisitos

Para poder seguir este tutorial correctamente, es necesario tener Vue Js instalado y crear un proyecto para ello ejecutaremos el siguiente comando:

vue create nombredelproyecto

En este caso yo trabajaré directamente sobre App. Además también habrá que instalar la librería Fuzzy, para ello sobre el proyecto creado de Vue insertaremos el siguiente comando:

npm i fuzzy

Esto instalara las dependencias necesarias para usar la librería Fuzzy en nuestro proyecto.

Después de realizar la instalación nos dirigiremos donde queremos incluir nuestro buscador. Y para usar la librería deberemos declarar la siguiente variable:

 fuzzy: require('fuzzy')

Esta variable es la que apuntará sobre las dependencias en las cuales se ha instalado la librería y así poder usar las funciones que esta contiene.

También será necesaria la etiqueta HTML <input> para recoger los datos escritos y enviarlos al buscador.

Por lo que tendría que quedarnos algo similar a lo siguiente.

<template>
  <div>
    <input type="text" v-model="texto">
    <button @click="buscadorfuzzy()">Buscar coincidencias</button>

</div>
</template>
<script>
export default {
  name: 'App',
  data(){
    return{
    fuzzy :require('fuzzy'),
  }},
  methods:{
    buscadorfuzzy(){
    }
  },
}
</script>


Uso del buscador

Para implementar este buscador es necesario recoger las palabras de la etiqueta <input> y enviarlas a la función filter de la librería la cual devolverá los datos ya filtrados. Para ello, también se dependerá de la lista de opciones.

La lista de opciones será necesaria si deseamos que en nuestra página web se devuelvan los resultados en negrita o si se va a trabajar mediante datos con un formato JSON. Será fundamental por lo menos declararla. La sintaxis es la siguiente:

var opciones= {
        pre: '<b>',
        post: '</b>',
        extract: function(elemento){
          return elemento.name
        }
      }

Donde las «keys» pre y post, serán las encargadas de devolver los datos en negrita.

La última de ellas (extract), será necesaria si se va a trabajar con datos en formato JSON, o que tienen una estructura compleja, ya que será más fácil tratarlos, este método no te devolverá los datos como un «objeto» sino que filtrará los datos y te devolverá solo la variable, la posición o el texto original de la misma, de esta manera:


{string: 'Lion King - Simb<b>a</b>', score: 1, index: 0, original: 'Lion King - Simba'}

Por ejemplo, si nosotros deseamos sacar el primer dato de este JSON:

{
      "apellido": "García",
      "dni":"11111111A"
}

Deberemos indicar en el return el nombre que le hemos dado en el function (elemento) seguido por la «key» apellido.

Después de esto ya se podrá usar el la función filter de la librería. Para ello deberemos de llamar a la variable la cual declaramos al principio de este post, y pasando lo parámetros indicados a continuación:

this.fuzzy.filter(texto introducido en la caja , JSON o strings a buscar ,  lista de opciones)

Esta función puede devolver dos tipos de datos según como estemos trabajando.

Si se trabaja con el «extract», deberemos sacar los datos de la siguiente manera, ya que devolverá la estructura ya explicada anteriormente:


 this.resultado = datosfiltrados.map(function(data) {
        return '<li>' + data.string + '</li>';
      });

Donde el .string será necesario para enseñar el «Object» devuelto.

Sin embargo, si se trabaja sin el extract, devolverá un Object con la estructura del mismo, aunque el autor recomienda el uso de extract ya que de esta forma la librería puede fallar. Por ejemplo:

for (let i = 0; i < elementofiltrado.length; i++) {
         this.resultado+='<li">'+elementofiltrado[i].nombre +'</p>
          <img src="'+elementofiltrado[i].imagen+'"/></li>';
}

Los elementos filtrados que devolverán ambos bucles también necesitarán una variable la cual será la encargada de pintar estos datos.

Por lo que el resultado de la función buscadorfuzzy() que podremos implementar en nuestra aplicación puede ser como este:

buscadorfuzzy() {
      this.resultado = "";
      var opciones = {
        pre: "<b>",
        post: "</b>",
        extract: function (elemento) {
          return elemento.name;
        },
      };
      var elementofiltrado = this.fuzzy.filter(this.texto,this.pokemon,opciones);
      for (let i = 0; i < elementofiltrado.length; i++) {
        this.resultado +=
          '<li> <p>' + elementofiltrado[i].string +'</p></li>';
      }
    }

Y listo, con esta ya podremos implementar el buscador, tendréis este ejemplo en el repositorio de GitHub, que se encuentra al final del post.

Funcionamiento de la librería

En este apartado antes de finalizar voy a explicar el proceso interno que desarrolla la librería a la hora de filtrar datos.

1

La primera función de la librería y la que nosotros llamamos directamente sería la función filter. Este función es la encargada de extraer datos y ver si coinciden.

  if(!arr || arr.length === 0) {
    return [];
  }

Esta función comprueba si el JSON esta vacío.

 if (typeof pattern !== 'string') {
    return arr;
  }
  

Este if comprueba si los datos recibidos son string.

  opts = opts || {};

Esta variable contiene un Coalescing Operator, es decir, esta variable comprueba si se le ha pasado un objeto opciones, si es nulo, crea uno vacío.

.reduce(function(prev, element, idx, arr) {
      var str = element;
      if(opts.extract) {
        str = opts.extract(element);
      }
      var rendered = fuzzy.match(pattern, str, opts);
      if(rendered != null) {
        prev[prev.length] = {
            string: rendered.rendered
          , score: rendered.score
          , index: idx
          , original: element
        };
      }
      return prev;
    }, [])

Si todas las condiciones anteriores se han cumplido devolverá el array, pero este será reducido, llamando a la función que se encargará de buscar si los datos coinciden.

  if(opts.extract) {
        str = opts.extract(element);
      }

Extrae los elementos de la función extract para saber el tipo de dato que tendrá que buscar y devolver.

 if(rendered != null) {
        prev[prev.length] = {
            string: rendered.rendered
          , score: rendered.score
          , index: idx
          , original: element
        };
      }
      return prev;
    }, [])

En el final de está función comprobará si ha encontrado algún resultado, si ha sido así devolverá una lista con los datos encontrados, sino devolverá una lista vacía.

2

Esta función es la encargada de realmente comparar los datos con lo tecleado en el <input>, es una función extensa , pero que en realidad solo lleva un contador para saber cuantos resultados han coincidido en total y comparando en minúscula.

 opts = opts || {};
  var patternIdx = 0
    , result = []
    , len = str.length
    , totalScore = 0
    , currScore = 0
    , pre = opts.pre || ''
    , post = opts.post || ''
    , compareString =  opts.caseSensitive && str || str.toLowerCase()
    , ch;
  pattern = opts.caseSensitive && pattern || pattern.toLowerCase();

El principio de está función es una simple declaración de variables, con algún Coalescing Operator, ya explicado anteriormente, también se puede comprobar que si no ha recibido ninguna variable pre o post, las creara vacías y no pondrá las coincidencias en negrita.

En las funciones compareString y pattern, comprueba si los valores introducidos son minúsculas o mayúsculas, encontrando en cualquiera de los dos casos el resultado.

for(var i = 0; i < len; i++) {
    ch = str[i];
    if(compareString[i] === pattern[patternIdx]) {
      ch = pre + ch + post;
      patternIdx += 1;
      currScore = 1 + currScore;
    } else {
      currScore = 0;
    }
    totalScore += currScore;
    result[result.length] = ch;
  }

En el bucle buscará las coincidencias de palabras y también llevará un contador de cuantas han coincidido.

if(patternIdx === pattern.length) {
    totalScore = (compareString === pattern) ? Infinity : totalScore;
    console.log(totalScore)
    return {rendered: result.join(''), score: totalScore};
  }
  return null;

Al final de la función comprobará si las longitudes son exactas y devolverá un diccionario de los resultados, juntando las letras, con el número máximo de coincidencias (no sin antes ser comprobado por un ternario). Si no se cumple la primera condición se devolverá un nulo.

Conclusión

El buscador que ofrece la librería Fuzzy, es muy útil a la hora de implementarlo a un proyecto, ya que este nos ahorrará tiempo, si se desea introducir en nuestra página web. La única pega que se le puede poner es un fallo a la hora de tratar los datos con el uso del extract, ya que según el autor esta tiene un fallo el cual no devuelve el tipo de dato que debería y devuelve el «objeto» del JSON.


Autor/a: Miguel Guerra Barriales

Curso: Desarrollo Web Full Stack + MultiCloud con Azure y AWS

Centro: Tajamar

Año académico: 2022-2023

Enlaces de interés:

Ejemplos de buscadores: https://github.com/MiguelXD13/fuzzyexamples

GitHub del autor de la librería: https://github.com/mattyork/fuzzy

Página de npm: https://www.npmjs.com/package/fuzzy

LinkedIn: https://www.linkedin.com/in/miguel-guerra-barriales-513485254/

Leave a Comment

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.