DEV Community

Cover image for Higher Order Functions
DevJoseManuel
DevJoseManuel

Posted on

Higher Order Functions

En este artículo vamos a ver qué son las Higher Order Functions y cómo podemos aplicarlas para lograr que nuestro código sea mucho más funcional. Pero antes de nada ¿qué es una Higher Order Function? Pues no es más que una función que toma como argumento una o más funciones o bien es una función que retorna a su vez otra función como resultado de su invocación.

Pero ¿esto cómo es posible? Pues lo primero que tenemos que pensar es que en JavaScript las funciones son un tipo de datos más como lo son los tipos primitivos o los objectos (lo que se conoce a su vez como first class citizens). Esto quiere decir que las funciones van a poder ser utilizadas el código como se utilizan el resto de los valores con los que podemos trabajar.

¿Y qué nos permite esto? Pues es una forma de lograr la composición de funciones dentro de nuestro código con el fin de lograr nuevas funcionalidades a partir de las que ya tenemos construidas.

Es más deberíamos pensar en el uso de las Higher Order Functions principalmente por dos razones. La primera de ellas es que van a hacer que nuestro código sea mucho más legible porque tras ellas quedarán ocultos varios detalles de implementación que no nos tienen que preocupar cuando las estemos utilizando y por otra parte nos permitirán lograr que nuestro código sea mucho más reutilizable.

Funciones sobre arrays.

Es más que probable que sin saberlo ya hayas utilizado Higher Order Functions cuando has estado programando en JavaScript puesto que las funciones map(), filter() o reduce() que nos proporciona el lenguaje para trabajar con los arrays en el código son consideradas Higher Order Functions.

Para entender cómo funcionan las Higher Order Functions vamos a centrarnos en primer lugar en estos métodos asociados a los arrays y para ello vamos a trabajar con los arrays. Para ello vamos a definir un array de números como sigue:

const data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Enter fullscreen mode Exit fullscreen mode

Si ahora queremos invocar al método map() sobre el array que acabamos de definir lo primero que tenemos que saber es que este método espera recibir como argumento una función que a su vez puede tener definidos hasta 3 argumentos siendo el primero de ellos el elemento del array sobre el que se está iterando, el segundo el índice que está asociado sobre ese elemento dentro de la iteración y el tercero el array en sí mismo.

De esta manera si lo que queremos lograr es un nuevo array que contenga los mismos elementos que el array data pero donde cada uno de ellos sea el elemento de data multiplicado por dos lo que escribiríamos sería algo como lo que se puede ver a continuación:

const data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
const mapped = data.map(n => n * 2)

console.log({ mapped })
Enter fullscreen mode Exit fullscreen mode

Si ahora ejecutamos nuestro código en la terminal del sistema nos vamos a encontrar algo parecido a lo que se puede ver en la siguiente imagen:

Higher Order Functions

Pensemos ahora en el argumento que le estamos pasando al método map() puesto que se trata a su vez de una función y como tal la vamos a poder extraer a una variable como sigue:

const double = n => n * 2
Enter fullscreen mode Exit fullscreen mode

es decir que estamos ante una función que tomará como argumento un número y lo que hará será devolvernos el valor de ese número multiplicado por dos (doblado). Ahora podemos aplicar esta función a nuestro código pasándola como argumento del método map() como sigue:

const data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

const double = n => n * 2
const mapped = data.map(double)

console.log({ mapped })
Enter fullscreen mode Exit fullscreen mode

y si volvemos a ejecutar nuestro código el resultado que vamos a obtener por la consola volverá a ser exactamente el mismo que habíamos obtenido anteriormente tal y como esperamos que así sea:

Una de las cosas que tenemos que asegurar cuando estamos pasando funciones como argumentos de las Higher Order Functions es que la función que vamos a pasar deberá seguir la firma (entiendo como tal, que sigue la definición de los argumentos) que a su vez espera la Higher Order Function y de esta manera que pueda ser aplicada como argumento.

¿Lioso? Seguro que sí porque es más difícil explicarlo de palabra que verlo con un ejemplo. En nuestro caso recordemos los tres argumentos que map() espera que la función que recibe como parámetro reciba:

por lo tanto la función double (la que estamos pasando a la Higher Order Function) deberá tratar el primero de sus parámetros como un número para que funcione correctamente. ¿Qué quiere esto decir? Pues que la función double podría tener una declaración completa en la que además del número recibiese el índice y el array sobre el que se está trabajando:

const double = (n, index, array) => n * 2
Enter fullscreen mode Exit fullscreen mode

Nota: en definitiva lo que estamos tratando de decir es que en el caso de que vayamos a definir una función para ser utilizada como parámetro de una Higher Order Function deberemos asegurarnos de que admitirá los argumentos con los que se debería operar.

Método filter()

Vamos a verlo ahora con el método filter() que nos proporcionan los arrays pero en este caso supongamos que vamos a definir la función que pasaremos como argumento a este método de tal manera que nos proporcione aquellos elementos del array que son número pares sabiendo que estos serán aquellos números que al dividirlos por 2 el resto que nos dejan será cero. Por lo tanto definimos:

const data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

const even = (n, index, array) => n % 2 === 0
const filtered = data.filter(even)

console.log({ filtered })
Enter fullscreen mode Exit fullscreen mode

Esta forma de trabajar es exactamente la misma que en el caso del método map() que hemos descrito anteriormente pero también podríamos haber pensado en crear nuestra propia implementación de una función filter que actúe como una Higher Order Function. Para ello vamos a comenzar definiendo una nueva función en nuestro código que llamaremos filter y que espera recibir dos argumentos: el primero de ellos un array y el segudo un callback function:

const filter = (arr, func) => {}
Enter fullscreen mode Exit fullscreen mode

Nota: recordemos que en la definición que hemos dado de una Higher Order Function hemos dicho que será cualquier función que tome como argumento una función por lo que en el caso de esta implementación de filter que estemos desarrollando estaremos ante una Higher Order Function.

Dentro del código de la función filter lo que vamos a hacer es definir un nuevo array que contendrá los resultados de aplicar la función filter y recorreremos todos los elementos del array que se ha recibido como argumento:

const filter = (arr, func) => {
  const result = []

  for (let i = 0; i < arr.length; i++ >) { }
}
Enter fullscreen mode Exit fullscreen mode

En cada una de las iteraciones del bucle lo que vamos a hacer es llamar a la callback función func a la que le vamos a pasar el elemento del array que se está procesando sabiendo que esta función ha de retornar true en el caso de que se cumpla el filtro o false en caso contrario. De esta manera si el resultado de la invocación de la función es truelo que tendremos que hacer será depositar el elemento que se está procesando en el array de resultados y en el caso de que sea false lo que haremos será saltárnoslo:

const filter = (arr, func) => {
  const result = []

  for (let i = 0; i < arr.length; i++ >) {
    if (func(arr[i])) {
      result.push(arr[i])
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Para terminar la implementación de nuestra función filter ya solamente nos quedará retornar el array con todos los resultados de que se han obtenido:

const filter = (arr, func) => {
  const result = []

  for (let i = 0; i < arr.length; i++ >) {
    if (func(arr[i])) {
      result.push(arr[i])
    }
  }

  return result
}
Enter fullscreen mode Exit fullscreen mode

Ahora que hemos construido nuestra propia implementación del método filter() de los arrays vamos a utilizarla en el código que estamos desarrollando y de hecho vamos a volver a desarrollar el ejemplo que habíamos hecho anteriormente y para ello definiremos la función even que servirá para obtener los elementos que son pares dentro de un array como sigue:

const even = filter(data, n => n % 2 === 0)
Enter fullscreen mode Exit fullscreen mode

Como vemos definimos la función even como el resulado de la función filter que esperará recibir un array de datos (en el argumento data) y como callback function le pasamos una que recibe un número como argumento y retornará true en el caso de se trate un número par o false si se trata de un número par.

Si además añadimos el código que nos permitirá escribir por la consola el resultado de que está recogido en even nos vamos a encontrar con algo como lo que se puede ver a continuación:

const data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

const filter = (arr, func) => {
  const result = []

  for (let i = 0; i < arr.length; i++ >) {
    if (func(arr[i])) {
      result.push(arr[i])
    }
  }

  return result
}

const even = filter(data, n => n % 2 === 0)

console.log({ even })
Enter fullscreen mode Exit fullscreen mode

Y si ejecutamos el código lo que obtendremos en la consola será algo parecido a lo que se puede ver en la siguiente imagen en la que efectivamente vemos que el array evenestá formado únicamente por los elementos que son pares dentro del array data tal y como esperábamos:

Por lo tanto ver que gracias al uso de las Higher Order Functions vamos a poder combinar varias funciones que están definidas dentro de nuestro código para lograr nuevas funcionalidades como cuando estamos usando filter() para quedarnos únicamente con los elementos pares de un array de números.

Top comments (0)