Conceptos básicos de JS: Array#filter

Array#filter, o Array.prototype.filter, es un método que poseen todos lo arrays en JavaScript. Este método nos sirve para filter un array y obtener uno nuevo, lo que en palabras más normales significa que vamos a recorrer un array y aplicar una condición a cada uno de sus elementos y solo quedarnos con los que pasen. Veamos un ejemplo:

const numbers = [1, 2, 3];
const evens = numbers.filter(number => number % 2 === 0); // es par
console.log(evens); // [2]

Lo que hace Array#filter entonces es llamar la función que pasamos como argumento una vez por cada elemento del array.

Nota: Ya que Array#filter devuelve un nuevo array, es una mala práctica usarlo para modificar manualmente el array inicial o hacer algún otro efecto al recorrer el array, para eso es mejor usar Array#forEach.

Si queremos hacer lo mismo que Array#filter tendríamos que hacer algo similar a esto:

const numbers = [1, 2, 3];
const evens = [];

for (const number of numbers) {
  if (number % 2 === 0) evens.push(number);
}

console.log(evens); // [2,4,6]

Tendríamos que crear un nuevo array vacío, recorrer el primero e ir haciendo Array#push de cada valor que pase nuestra condición

Sintaxis

La sintaxis completa de la función es la siguiente

const result = initial.filter(function callback(currentValue, index, array) {
  // Devuelve true o false para saber si debe quedarse o no
}, thisArg);

Array#filer recibe dos argumentos, una función callback y un valor, cualquiera, que va a ser asignado como this del callback, en caso de usar arrow functions este valor no hace nada. El callback a su vez recibe tres argumentos, la mayoría de las veces se suele usar solo el primero y algunas veces el segundo, el primero es el valor actual por el que estamos pasando en la iteración, el segundo es el índice dentro de nuestro array y el tercero es el mismo array que estamos iterando.

Por último, la función devuelve un nuevo array con los resultados de haber ejecutado callback una vez por cada elemento del array inicial.

Implementando Array#filter a mano

Veamos ahora como crear nuestro propio Array#filter para entender mejor su funcionamiento. Vamos a implementarlo como una función llamada filter cuyo primer valor va a ser el array a filtear y después vamos a recibir el callback y el valor de this.

function filter(array, callback, thisArg) {
  const result = [];
  const boundCallback = thisArg ? callback.bind(thisArg) : callback;
  for (let index = 0; index < array.length; index++) {
    if (boundCallback(array[index], index, array)) result.push(array[index]);
  }
  return result;
}

const numbers = [1, 2, 3];
const evens = filter(numbers, number => number % 2 === 0);
console.log(evens); // [2,4,6]

¿Qué es lo que hicimos? Nuestra función recibe los tres argumentos que dijimos antes, luego crea un array vacío llamado result y una constante llamada boundCallback que en caso de que se haya definido thisArg es igual a hacer Function#bind de callback con el valor de thisArg y en caso contrario es igual a callback. Después iteramos el array inicial y hacemos result.push si el valor que devuelve callback es true, al cual le pasamos el valor actual del array, el índice y el array entero. Por último devolvemos el resultado.

Casos de uso

Array#filter es usado un montón en JavaScript, especialmente cuando se trabaja de una forma funcional y en combinación con otros métodos como Array#map. Es usado en React.js para filtras listas de datos en elementos de React, por ejemplo:

function List({ list, query }) {
  return (
    <ul>
      {list
        .filter(item => item.content.includes(query))
        .map(item => <Item key={item.id} {...item} />)
      }
    </ul>
  );
}

ReactDOM.render(
  <List list={[{ id: 1, content: "hola" }, { id: 2, content:  }]} query="hola" />,
  target
);

Este componente List usa Array#filter para filtrar según el contenido de cada elemento y Array#map para transformar cada item que queda después del filtro del array list en el elemento devuelto por el componente Item pasándole toda la información de cada item al componente.


Fuera de React puede ser usado también para trabajar con listas de elementos del DOM.

const $selectList = document.querySelectorAll("select");
const values = Array.from($selectList)
  .map($select => $select.value)
  .filter(value => value !== "");

Lo que hace el ejemplo es obtener todos los <select> de una página lo que devuelve un objeto NodeList, usando Array.from lo convertimos a un array y usamos Array#map para obtener una lista (values) de los valores de cada <select>, por último con Array#filter eliminamos los valores igual a un string vacío, que podría ser considerado la opción por defecto.

Palabras finales

Este método es muy útil y es usado a diario al trabajar con Arrays en JavaScript. Si no lo habías usado antes te recomiendo probar varios ejemplos de uso para familiarizarte, cuando lo hagas vas a querer usarlo siempre, especialmente en combinación de Array#map y Array#reduce.