Como ya vimos en first-class functions, a la función o funciones que reciben como parámetro otra función, que devuelven una función o ambas, se les denomina higher-order functions.

Veamos un ejemplo de una función que devuelve a partir de un array de nombres de ciudades, las ciudades que tienen nombre compuesto:

const ciudadesNombreCompuesto = (ciudades) => {
  const ciudadesNombreCompuesto = [];

  for (let i = 0; i < ciudades.length; i++) {
    const ciudad = ciudades[i];
    if (ciudad.split(' ').length > 1) {
      ciudadesNombreCompuesto.push(ciudad);
    }
  }

  return ciudadesNombreCompuesto;
};

ciudadesNombreCompuesto([
  'Madrid',
  'Miami',
  'Nueva York',
  'Sidney',
  'Wellington',
]);
// ['Nueva York']

A partir de esa misma lista de ciudades, ahora queremos obtener todas las ciudades cuyo nombre empieza por ‘M’:

const ciudadesEmpiezanM = (ciudades) => {
  const ciudadesEmpiezanM = [];

  for (let i = 0; i < ciudades.length; i++) {
    const ciudad = ciudades[i];
    if (ciudad.startsWith('M')) {
      ciudadesEmpiezanM.push(ciudad);
    }
  }

  return ciudadesEmpiezanM;
};

ciudadesEmpiezanM(['Madrid', 'Miami', 'Nueva York', 'Sidney', 'Wellington']);
// ['Madrid', 'Miami']

Salta a la vista que ambos scripts tienen código repetido. Se puede reconocer un patrón que pide a gritos ser convertido en una solución mas abstracta y general. Ambas funciones iteran sobre una lista de ciudades y le aplican un filtro para acabar obteniendo una lista filtrada con igual o menos ciudades que la lista original recibida.

El uso de first-class functions nos hace mas fácil el proceso de abstracción. Por lo tanto, vamos a empezar creando una función que abstraiga la iteración sobre la lista y que devuelva esa misma lista igual o reducida, en este caso, por ejemplo, a una cadena de ciudades en mayúsculas.

const reduce = (ciudades, reducer, valorInicial) => {
  let acumulado = valorInicial;

  for (let i = 0; i < ciudades.length; i++) {
    const ciudad = ciudades[i];
    acumulado = reducer(acumulado, ciudad);
  }

  return acumulado;
};

reduce(
  // Lista de ciudades
  ['Madrid', 'Miami', 'Nueva York', 'Sidney', 'Wellington'],

  // Función redutora
  (acumulado, el) => acumulado + el.toUpperCase(),

  // Valor inicial
  ''
);
// MADRIDMIAMINUEVA YORKSIDNEYWELLINGTON

Una vez tenemos la función para abstraer la iteración ya podemos centrarnos en crear el filtro, que es la única parte diferente del código superior. Para ello, crearemos una función de filtro que recibe un array (en este caso de ciudades) y la función por la cual queremos filtrar cada uno de los elementos del array. Hay que tener en cuenta que en este caso, lo que queremos es obtener una lista de igual o menor tamaño en lugar de un único valor. Por este motivo, la función reducer siempre y cuando se cumpla el filtro añadira los elementos al listado de acumlados.

const reduce = (array, reducer, valorInicial) => {
  let acumulado = valorInicial;

  for (let i = 0; i < array.length; i++) {
    acumulado = reducer(acumulado, array[i]);
  }

  return acumulado;
};

const filter = (array, filtro) =>
  reduce(
    array,
    (acumulado, el) => (filtro(el) ? acumulado.concat([el]) : acumulado),
    []
  );

filter(
  // Lista de ciudades
  ['Madrid', 'Miami', 'Nueva York', 'Sidney', 'Wellington'],

  // Función filtro
  (ciudad) => ciudad.split(' ').length > 1
);
// ['Nueva York']

filter(
  // Lista de ciudades
  ['Madrid', 'Miami', 'Nueva York', 'Sidney', 'Wellington'],

  // Función filtro
  (ciudad) => ciudad.startsWith('M')
);
// ['Madrid', 'Miami']

Sin lugar a dudas las higher-order functions son una herramienta muy pontente a la hora de abstraer y simplificar el código.

Algunos ejemplos del propio lenguaje, como ya habrás podido notar son las funciones map(), filter() o reduce().

const ciudades = ['Madrid', 'Miami', 'Nueva York', 'Sidney', 'Wellington'];

ciudades.reduce((acumulado, el) => acumulado + el.toUpperCase(), '');
// MADRIDMIAMINUEVA YORKSIDNEYWELLINGTON

ciudades.filter((ciudad) => ciudad.split(' ').length > 1);
// ['Nueva York']

ciudades.filter((ciudad) => ciudad.startsWith('M'));
// ['Madrid', 'Miami']

Seguir leyendo:

Fuentes: