Foro de elhacker.net

Programación => Desarrollo Web => Mensaje iniciado por: @XSStringManolo en 31 Marzo 2021, 02:46 am



Título: [javascript] Métodos map, reduce y filter explicados. Extendiendo String para añadirle map.
Publicado por: @XSStringManolo en 31 Marzo 2021, 02:46 am
Map, reduce y filter
A muchos programadores les cuesta entender o encontrar uso para el método
Código
  1. .map()
En este post vamos a programar el método, así podrás entender exactamente que es.

map
Para no ensuciar el array de javascript, vamos a crear una nueva versión del array extendiendo la clase.
Código
  1. class CustomArray extends Array {
  2.  map() {
  3.    return "metodo custom";                                              
  4.  }
  5. }

Ahora tenemos 2 clases array iguales. La de javascript y la nuestra. La nuestra es casi igual que la de javascript, solo cambia el método map.
Puedes comprobar que funciona con el siguiente código
Código
  1. class CustomArray extends Array {
  2.  map() {
  3.    return "metodo custom";
  4.  }
  5. }
  6.  
  7. let numeros = new CustomArray(1, 2, 3); // Crea un array custom
  8. let numeros2 = [1, 2, 3]; // Crea un array normal
  9. numeros.map( num => num + 1); // "metodo custom"
  10. numeros2.map( num => num + 1); // 2, 3, 4

En la primera llamada a map (nuestro método custom) no pasa nada especial porque nuestro método no tiene definido que acepte ningún parámetro, asique se ejecuta el método sin más ignorando los parámetros. En cambio en la segunda llamada a map (el .map() original de javascript) se toma la función como argumento y se le suma 1 a cada elemento del array.

Una función que se pasa por parámetro a otra función/método (con intención de que se ejecute dentro de la función llamada) se le conoce como callback.                                                               
Vamos a hacer que nuestro método acepte una función y la llame:
Código
  1. class CustomArray extends Array {
  2.  map(callback) { // acepta un parametro cualquiera
  3.    return callback("metodo custom"); // llamalo pasandole el texto como argumento
  4.  }
  5. }
  6.  
  7. let numeros = new CustomArray(1, 2, 3); // Crea un array custom
  8. let numeros2 = new Array(1, 2, 3); // Crea un array normal
  9.  
  10. numeros.map( num => num + 1 ); // "metodo custom1"
  11. numeros2.map( num => num + 1 ); // 2, 3, 4

Así añadimos el callback. Aceptamos que se pase una función como argumento de map, llamamos la función que se pasa usando nombreArgumento(); y le pasamos al argumento un argumento en la llamada tal que nombreArgumento("método custom");
Actualmente nuestro map solo añade 1 a nuestro texto, pero el objetivo es que sume 1 a todos los elementos del array. Para ello usaremos this para acceder al CustomArray y sus elementos:
Código
  1. class CustomArray extends Array {
  2.  map(callback) {
  3.    return callback(this);
  4.  }
  5. }
  6.  
  7. let numeros = new CustomArray(1, 2, 3);
  8. let numeros2 = new Array(1, 2, 3);
  9.  
  10. numeros.map( num => num + 1 ); // "1, 2, 31"
  11. numeros2.map( num => num + 1 ); // 2 , 3, 4

Al usar this como parámetro del callback accedemos al array y como no.se pueden sumar arrays y números, javascript opta por un tipo intermedio entre ambos que se pueda sumar, sumándose los strings "1, 2, 3" + "1". Lo que hace map es llamar a la función que le pasamos sobre cada elemento y nos retorna un array con los resultados. Asique acabamos el map con:
Código
  1. class CustomArray extends Array {
  2.  map(callback) {
  3.    for (let i = 0; i < this.length; ++i) {
  4.      this[i] = callback(this[i])
  5.    }
  6.    return this;
  7.  }
  8. }
  9.  
  10. let numeros = new CustomArray(1, 2, 3);
  11. let numeros2 = new Array(1, 2, 3);
  12.  
  13. numeros.map( num => num + 1 ); //2, 3, 4
  14. numeros2.map( num => num + 1); //2, 3, 4
Ya conseguimos crear nuestra versión del método map. Map es un método de los arrays, por lo que no puedes usarlo con otras clases como String.
Puedes modificar el código para extender la clase String y añadirle el método map:
Código
  1. class CustomString extends String {
  2.  map(callback) {
  3.    let aux = "";
  4.    for (let i = 0; i < this.length; ++i) {
  5.      aux += callback(this[i])
  6.    }
  7.    return aux;
  8.  }
  9.  
  10. }
  11.  
  12. const texto = new CustomString("hola");
  13.  
  14. texto.map(letra => letra + 1) // "h1o1l1a1"

Creo que queda claro que hace exactamente map. Aplica la funcion que le pasamos a cada elemento y nos retorna el resultado.
Es el mismo efecto que si hiciésemos:
Código
  1. let arr = [1, 2, 3];
  2. let auxiliar = [];
  3. const suma1 = argumento => argumento + 1;
  4.  
  5. auxiliar.push( suma1(arr[0]) );
  6. auxiliar.push( suma1(arr[1]) );
  7. auxiliar.push( suma1(arr[2]) );
  8.  
  9. console.log(auxiliar) // 2, 3, 4


reduce
El método reduce es prácticamente igual que map, pero en lugar de devolver todos los elementos devuelve solo el resultado de operar con ellos.
Código
  1. let numeros = [1, 2, 3];
  2. numeros.reduce( (res, num) => res + num); // 6
El código también es muy similar al de map, trata de realizar tu propia implementación y jugar pásandole distintos parámetros. Recuerda que estos ejemplos son básicos. Los métodos map, reduce y similares son más completos que estas implementaciones, puedes consultar algunos polyfills para hacerte una idea más cercana a todo lo que hace cada método.

filter
Con filter en lugar de retornar el resultado, se retorna o no el elemento del array dependiendo de si la condición que indiques se cumple o no. Por ejemplo puedes filtrar los elementos de un array para obtener solo los números pares:
Código
  1. let numeros = [1, 2, 3];
  2. numeros.filter( num => num % 2 == 0 ); // 2

Recuerda que se retorna un nuevo array, el original no es modificado.


Título: Re: [javascript] Métodos map, reduce y filter explicados. Extendiendo String para añadirle map.
Publicado por: MinusFour en 31 Marzo 2021, 05:07 am
Realmente no hace falta crear una clase que extenda de Array para volver a implementar map.

La forma en la que usas map tipicamente es:

Código
  1. arr.map(fn);

Porque map en este caso es un método en la cadena del prototipo del arreglo. En este caso, el método usa this para acceder al objeto a iterar pero es más sencillo si lo pasamos como parametro directamente:

Código
  1. map(fn, arr);

Está función puede ser escrita como:

Código
  1. function map(fn, arr){
  2.    let res = [];
  3.    for(let i = 0; i < arr.length; i++){
  4.        res[i] = fn(arr[i], i);
  5.   }
  6.   return res;
  7. }

Y por supuesto puedes escribir una función map para cada tipo de dato, como strings. En programación funcional es uno de los métodos más comunes para casi todo lo que te pudieras encontrar.


Título: Re: [javascript] Métodos map, reduce y filter explicados. Extendiendo String para añadirle map.
Publicado por: @XSStringManolo en 31 Marzo 2021, 05:11 am
Ohhh, es bastante más smart usar un solo map en forma de función para cada tipo de dato. Por qué no harían eso?


Título: Re: [javascript] Métodos map, reduce y filter explicados. Extendiendo String para añadirle map.
Publicado por: MinusFour en 31 Marzo 2021, 13:18 pm
Ohhh, es bastante más smart usar un solo map en forma de función para cada tipo de dato. Por qué no harían eso?

¿Porque no hace eso el lenguaje? Porque es un lenguaje orientado a objetos primero, antes de ser un lenguaje enfocado a programación funcional. Lenguajes como Haskell tienen un sistema que se aprecia más a funciones (en este caso, Functors).