Cómo usar el filtro para filtrar dinámicamente una matriz en JavaScript

Cómo utilizar el método Array.Filter de JavaScript para filtrar o eliminar elementos de forma selectiva de una matriz.

Primeros pasos

Para este tutorial, vamos a utilizar el marco JavaScript de pila completa de CheatCode, Joystick. Joystick reúne un marco de interfaz de usuario de front-end con un back-end de Node.js para crear aplicaciones.

Para comenzar, querremos instalar Joystick a través de NPM. Asegúrese de estar usando Node.js 16+ antes de instalar para garantizar la compatibilidad (lea este tutorial primero si necesita aprender a instalar Node.js o ejecutar varias versiones en su computadora):

Terminal

npm i -g @joystick.js/cli

Esto instalará Joystick globalmente en su computadora. Una vez instalado, vamos a crear un nuevo proyecto:

Terminal

joystick create app

Después de unos segundos, verá un mensaje desconectado de cd en su nuevo proyecto y ejecute joystick start :

Terminal

cd app && joystick start

Después de esto, su aplicación debería estar ejecutándose y estamos listos para comenzar.

Cableando una interfaz de usuario

Primero, antes de filtrar nuestra matriz, configuraremos una interfaz de usuario para contextualizar nuestro trabajo. Nuestro objetivo será crear una lista de álbumes de música que podamos filtrar según el género de cada álbum.

En la aplicación que acabamos de crear cuando ejecutamos joystick create app , se creó un componente de ejemplo para nosotros en /ui/pages/index/index.js . Abramos eso ahora y reemplacemos los contenidos existentes con el esqueleto de nuestra interfaz de usuario de filtrado.

/ui/pages/index/index.js

import ui from '@joystick.js/ui';

const albums = [
  { id: 1, artist: 'Queens of the Stone Age', title: 'Songs for the Deaf', year: 2002, genre: 'rock' },
  { id: 2, artist: 'David Bazan', title: 'Havasu', year: 2022, genre: 'rock' },
  { id: 3, artist: 'Dwight Yoakam', title: 'This Time', year: 1993, genre: 'country' },
  { id: 4, artist: 'Sion', title: 'Sion', year: 2021, genre: 'metal' },
  { id: 5, artist: 'Every Time I Die', title: 'Low Teens', year: 2016, genre: 'metal' },
  { id: 6, artist: 'Cannonball Adderley', title: 'Somethin\' Else', year: 1958, genre: 'jazz' },
  { id: 7, artist: 'The Bad Plus', title: 'Suspicious Activity?', year: 2005, genre: 'jazz' },
  { id: 8, artist: 'Cory Hale', title: 'Soft', year: 2020, genre: 'electronic' },
  { id: 9, artist: 'Rezz', title: 'Spiral', year: 2021, genre: 'electronic' },
  { id: 10, artist: 'Autolux', title: 'Future Perfect', year: 2004, genre: 'experimental' },
];

const Index = ui.component({
  state: {
    filteredAlbums: albums,
  },
  render: ({ each, state }) => {
    return `
      <div>
        <ul>
          ${each(state.filteredAlbums, (album) => `
            <li>${album.artist} - ${album.title} (${album.year}) <span>#${album.genre.toLowerCase()}</span></li>
          `)}
        </ul>
      </div>
    `;
  },
});

export default Index;

Aquí, estamos escribiendo un componente usando el @joystick.js/ui biblioteca que es parte del marco Joystick. Los componentes son funciones que devuelven una cadena de HTML que se representa en el navegador del usuario y se actualiza cada vez que hay datos dinámicos dentro el componente cambia.

Para comenzar, estamos haciendo dos cosas importantes aquí:justo debajo de nuestra importación de ui objeto del @joystick.js/ui paquete, estamos definiendo una matriz de objetos con cada objeto representando un álbum en nuestra lista. Hemos creado esto aquí porque necesitamos poder hacer referencia a una copia no modificada de nuestra lista (esto tendrá más sentido en un momento).

A los state propiedad en el objeto de opciones que pasamos a nuestro ui.component() definición, estamos configurando una propiedad filteredAlbums al albums formación. Esta es la configuración predeterminada valor para el filteredAlbums propiedad en estado. El estado son datos temporales dentro de nuestro componente que solo existen hasta que se actualiza la página.

Bajando al render función, somos return ing una cadena de HTML que está representando un <ul></ul> (lista desordenada) que mostrará nuestra lista de álbumes. Para enumerar esos elementos, usando la desestructuración de JavaScript en el primer argumento pasado al render función:arrancamos el each y state propiedades de nuestra instancia de componente (esta instancia es el primer argumento pasado al render función).

Dentro del HTML para nuestro <ul></ul> etiqueta, estamos usando la interpolación de JavaScript para decir "en este punto de la cadena, inyecte este valor". El valor que queremos inyectar es el resultado de llamar al each() función que acabamos de extraer de la instancia del componente (devuelve una cadena de HTML).

A ese each función, pasamos una matriz de elementos, en este caso nuestro filteredAlbums valor de state junto con una función que se llamará para cada elemento de la matriz. Se espera que esa función devuelva una cadena de HTML para cada valor en la matriz. Aquí, cada elemento de la matriz es un album y queremos devolver un <li></li> (elemento de la lista) para cada álbum.

Dentro de esa función, devolvemos una cadena idéntica al return principal para nuestro render función, pasando nuestro <li></li> etiqueta rellenada con las partes de nuestro álbum, utilizando la misma etiqueta de interpolación de JavaScript ${} acabamos de aprender a inyectar valores en la cadena. El resultado final de la ejecución de este código se verá así:

<ul>
  <li>Queens of the Stone Age - Songs for the Deaf (2002) <span>#rock</span></li>
  <li>David Bazan - Havasu (2022) <span>#rock</span></li>
  <li>Dwight Yoakam - This Time (1993) <span>#country</span></li>
  ...
</ul>

Ahora que tenemos la representación de nuestra lista, a continuación, queremos comenzar a cablear nuestro filtrado. Para hacerlo, primero, necesitamos tener un mecanismo por el cual realmente filtraremos nuestra lista.

Conectando una matriz para filtrar

Como insinuamos anteriormente, nuestro objetivo es filtrar nuestra matriz de álbumes por género . Para iniciar ese proceso, ahora vamos a agregar una función de método personalizado que recupera el género de cada uno de nuestros álbumes y lo llena en un <select></select> etiqueta que usaremos para filtrar nuestra lista.

/ui/pages/index/index.js

import ui from '@joystick.js/ui';

const albums = [
  { id: 1, artist: 'Queens of the Stone Age', title: 'Songs for the Deaf', year: 2002, genre: 'rock' },
  { id: 2, artist: 'David Bazan', title: 'Havasu', year: 2022, genre: 'rock' },
  { id: 3, artist: 'Dwight Yoakam', title: 'This Time', year: 1993, genre: 'country' },
  { id: 4, artist: 'Sion', title: 'Sion', year: 2021, genre: 'metal' },
  { id: 5, artist: 'Every Time I Die', title: 'Low Teens', year: 2016, genre: 'metal' },
  { id: 6, artist: 'Cannonball Adderley', title: 'Somethin\' Else', year: 1958, genre: 'jazz' },
  { id: 7, artist: 'The Bad Plus', title: 'Suspicious Activity?', year: 2005, genre: 'jazz' },
  { id: 8, artist: 'Cory Hale', title: 'Soft', year: 2020, genre: 'electronic' },
  { id: 9, artist: 'Rezz', title: 'Spiral', year: 2021, genre: 'electronic' },
  { id: 10, artist: 'Autolux', title: 'Future Perfect', year: 2004, genre: 'experimental' },
];

const Index = ui.component({
  state: {
    filteredAlbums: albums,
  },
  methods: {
    getAlbumGenres: () => {
      const genres = albums.map(({ genre }) => {
        const capitalizedGenre = genre.charAt(0).toUpperCase() + genre.slice(1);
        return capitalizedGenre;
      });

      return Array.from(new Set(genres));
    },
  },
  render: ({ each, state, methods }) => {
    return `
      <div>
        <select>
          <option value="all">All</option>
          ${each(methods.getAlbumGenres(), (genre) => {
            return `
              <option value="${genre.toLowerCase()}">${genre}</option>
            `;
          })}
        </select>
        <ul>
          ${each(state.filteredAlbums, (album) => `
            <li>${album.artist} - ${album.title} (${album.year}) <span>#${album.genre.toLowerCase()}</span></li>
          `)}
        </ul>
      </div>
    `;
  },
});

export default Index;

Centrándose en el methods propiedad que agregamos a las opciones que estamos pasando a ui.component() , aquí hemos agregado un método (una función miscelánea a la que puede llamar en un componente Joystick) llamado getAlbumGenres .

Cuando lo llamamos, creamos una variable llamada genres que se establece en JavaScript .map() que utiliza la desestructuración de JavaScript para extraer el genre propiedad de cada objeto en nuestro albums matriz en la parte superior de nuestro archivo. Dentro del mapa, escribimos en mayúscula el nombre del género (en nuestro albums array está en minúsculas) para mostrar en nuestra interfaz de usuario.

Una vez que tengamos capitalizedGenre , lo devolvemos desde el .map() . Esto debería darnos algo como esto:

['Rock', 'Rock', 'Country', 'Metal', 'Metal', 'Jazz', 'Jazz', 'Electronic', 'Electronic', 'Experimental']

Esto nos lleva en parte al camino, pero hay un problema obvio:tenemos muchos duplicados. Para evitar esto, después de que tengamos nuestra matriz en el genres variable, de nuestro getAlbumGenres() función usamos el new Set() constructor de clases, pasando nuestro genres variable. Esto nos dará un conjunto de JavaScript a cambio, que es un objeto que solo contiene valores únicos, como este:

{ 'Rock', 'Country', 'Metal', 'Jazz', 'Electronic', 'Experimental' }

Aunque puede que no parezca un objeto JavaScript tradicional (un conjunto de pares clave/valor), JavaScript lo reconoce como tal (un Conjunto es solo un tipo especial de Objeto, extendido del prototipo de Objeto principal en el lenguaje). Si bien esto nos acerca un paso más, debido a que necesitamos poder recorrer esta lista, tomamos el resultado de llamar a new Set(genres) y pásalo directamente a Array.from() para recuperar nuestra lista única como una matriz:

['Rock', 'Country', 'Metal', 'Jazz', 'Electronic', 'Experimental']

Volviendo a centrarnos en el render función, podemos ver que hemos agregado en el <select></select> etiqueta que insinuamos anteriormente. Dentro, agregamos un <option></option> etiqueta con un valor de all y contenido de texto de "Todos" (esto nos permitirá regresar a la lista completa desde una lista filtrada).

Justo debajo de esto, usamos el each() función de nuevo junto con la interpolación de JavaScript para recorrer el resultado de llamar al methods.getAlbumGenres() función que acabamos de escribir (observe que hemos agregado methods como uno de los valores que estamos extrayendo de la instancia del componente pasado al render función).

Por cada genre , generamos un <option></option> etiqueta con la versión en minúsculas del genre para el value atributo y el genre en mayúscula para el contenido del texto.

Casi termino. Ahora, estamos listos para filtrar nuestra lista. Para hacerlo, agregaremos un detector de eventos en el <select></select> etiqueta que acabamos de agregar a nuestro render :

/ui/pages/index/index.js

import ui from '@joystick.js/ui';

const albums = [ ... ];

const Index = ui.component({
  state: {
    filteredAlbums: albums,
  },
  methods: { ... },
  events: {
    'change select': (event, component) => {
      const filterByGenre = event.target.value;
      component.setState({
        filteredAlbums: filterByGenre === 'all' ? albums : albums.filter((album) => {
          return album.genre === filterByGenre;
        })
      });
    },
  },
  render: ({ each, state, methods }) => {
    return `
      <div>
        <select>
          <option value="all">All</option>
          ${each(methods.getAlbumGenres(), (genre) => {
            return `
              <option value="${genre.toLowerCase()}">${genre}</option>
            `;
          })}
        </select>
        <ul>
          ${each(state.filteredAlbums, (album) => `
            <li>${album.artist} - ${album.title} (${album.year}) <span>#${album.genre.toLowerCase()}</span></li>
          `)}
        </ul>
      </div>
    `;
  },
});

export default Index;

Tiempo para la parte importante. Aquí, hemos agregado un events opción a nuestro ui.component() y en él, hemos definido un detector de eventos que dice "cuando el change el evento se detecta en cualquier select elemento en el componente, llame a la función que se asigna aquí."

Dentro de esa función, para el primer argumento recibimos el objeto de evento DOM que se crea cuando change ocurre un evento y el component instancia como segundo argumento.

Antes de realizar nuestro filtro, primero nos aseguramos de seleccionar el género por el que estamos tratando de filtrar:este será el value propiedad del <select></select> o el event.target donde se originó el evento y almacenarlo en el filterByGenre variables.

A continuación, hacemos una llamada a component.setState() pasando un objeto de propiedades que queremos modificar en el state de nuestro componente , en este caso, filteredAlbums . Lo que configuramos filteredAlbums depende del valor de filterByGenre . Si filterByGenre está establecido en all entonces nosotros no desea filtrar nuestra lista. Aquí, usamos un operador ternario de JavaScript para decir si el valor es 'all' , devuelve el albums intacto formación. De lo contrario, o bien : queremos llamar al .filter() método en el albums matriz.

A albums.filter() pasamos una función que se llama para cada elemento de la matriz. Si el valor devuelto por esa función es un booleano true , el elemento se mantendrá en la matriz. Si el valor devuelto es un booleano false , se filtrará fuera de la matriz. Toma, para obtener ese true o false valor, verificamos si el .genre propiedad del álbum que estamos reproduciendo actualmente coincide con filterByGenre . Si lo hace, es un guardián. Si no es así, lo tiramos.

Porque estamos pasando nuestra llamada al albums.filter() directamente al filteredAlbums en el objeto pasamos a component.setState() , asumiendo que nuestro usuario no ha seleccionado el all opción, actualizaremos filteredAlbums en el estado para contener solo una matriz de objetos donde el genre propiedad en cada objeto coincide con lo que se seleccionó en el <select></select> lista de géneros.

En un componente Joystick, cambia a state activar una nueva representación, es decir, nuestro render() La función se volverá a llamar inmediatamente después de nuestra llamada a component.setState() , pasando el nuevo state.filteredAlbums valor.

¡Eso es todo! Ahora, si miramos en el navegador, deberíamos poder filtrar nuestra lista como se esperaba:

Terminando

En este tutorial, aprendimos a filtrar una matriz utilizando el método Array.Filter de JavaScript. Para contextualizar nuestro trabajo, creamos una interfaz de usuario usando el @joystick.js/ui biblioteca del marco Joystick para ayudarnos a generar una lista de álbumes para filtrar, junto con una entrada de selección que podríamos usar para filtrar esa lista por género.