Higher Order Functions
Tags: javascript, spanish
Published: 2024-04-29 08:47:55

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:

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

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.

![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mx0368a58szi88j0a8wo.png)

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:

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

console.log({ mapped })
```

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:

![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tn1ollsc6tczf0beqzyw.png)

### 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:

```tsx
const double = n => n * 2
```

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:

```tsx
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 })
```

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:

![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rt2b9yzcyqceod8tlvuj.png)

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:

![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2l02968yixh5dcds3ef4.png)

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:

```tsx
const double = (n, index, array) => n * 2
```

> **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:

```tsx
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 })
```

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:

```tsx
const filter = (arr, func) => {}
```

> **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:

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

  for (let i = 0; i < arr.length; i++ >) { }
}
```

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 `true`lo 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:

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

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

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:

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

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

  return result
}
```

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:

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

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:

```tsx
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 })
```

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 `even`está formado únicamente por los elementos que son pares dentro del array `data` tal y como esperábamos:

![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6yd0aong88dsaqa7tmtc.png)

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.