Ramda.js en el dia a dia (1)

20 enero 2021 0 Por Juan Martin

Ramda.js

Ramda.js es una libreria de corte funcional que nos puede aportar mucho en el desarrollo diario de nuestras aplicaciones, tanto si estás en el backend como si lo haces en el frontend.
La librería aplica con esmero los postulados del paradigma funcional, pudiendo considerar como «puras» todas sus funciones. Una función pura en dicho paradigma es aquella que solo opera con los parámetros pasados y que no hace ningún cambio en el sistema ni aplica ningún tipo de efecto. Además trata de inmutables los datos que recibe, entregando siempre versiones nuevas de la información que maneja.
Muchas funciones tienen su reflejo en el estándar de Javascript, como puede ser filter o map, por lo que aqui no las abordare. Me centraré más en las funciones que requieren de una implementación en el dia a dia como puede ser agrupar los objetos de una lista o realizar una ordenación por algun criterio. La serie constara de dos partes, sin ninguna orden especial.

allPass

Nuestro primer caso de uso se refiere a un aspecto que como desarrolladores backend nos encontramos muy a menudo como es la validación de los datos que recibimos en el body.
Igual no queremos o no encontramos o simplemente queremos nuestra propia librería de validaciones y aquí Ramda.js nos puede ayudar en la tarea.

proEq permite comparar una propiedad de un objeto con un valor. A efectos de nuestro ejemplo queremos que no haya campos nulos por lo que nos creamos funciones que se encargaran de devolvernos True si el campo es nulo.


const nombreIsNil = R.propEq('nombre', null );
const apellidosIsNil = R.propEq('apellidos', null);
const telefonoIsNil = R.propEq('telefono', null);
const direccionIsNil = R.propEq('direccion', null);

Tenemos que hacerlo así porque Javascript no tiene un valor para el «no nulo» y porque propEq solo admite valores y no funciones.

nombreIsNil nos devuelve True si el campo nombre es nulo podemos obtener la función complementaria con la funcion complement de Ramda


const nombreValido = R.complement(nombreIsNil);
const apellidosValido = R.complement(apellidosIsNil);
const telefonoValido = R.complement(telefonoIsNil);
const direccionValida = R.complement(direccionIsNil);

Estas cuatro funciones nos retornan True si cada propiedad no es nula. Pero pongamos un ejemplo más real y comprobemos el formato de un numero de telefono. Por supuesto de forma básica, solo miraremos que su longitud esté entre dos valores.


const formatoTelefono = R.propSatisfies(v => (v.length > 5 && v.length < 10), 'telefono');

Lo hacemos con propSatisfies que recibe un predicado, la propiedad a pasar al predicado y el objeto que va a comprobar, que en nuestro ejemplo esta currificado, recibiendo este parámetro más tarde.

La validación de todas las propiedades la hacemos con allPass que recibe un array de predicados y en caso de resolverse todas como verdaderas retornara true y false en caso de que al menos una falle.


const validaObjeto = R.allPass([nombreValido, apellidosValido, telefonoValido, direccionValida, formatoTelefono]);

clone

Muchas veces necesitamos devolver un clon de un objeto. La funcion clone de Ramda.js hace precisamente eso, clonando de forma profunda un objeto, incluyendo arrays y otros objetos. Si el objeto tuviera funciones serian clonadas por referencia.


let copia = R.clone(request);

sort y comparator

Si necesitamos ordenar por algun criterio arbitrario una lista de objetos podemos recurrir a estas dos funciones de Ramda.js para la tarea.

comparator nos permite definir una funcion que nos diga si el primer elemento es menor que el segundo. sort permite ordenar la lista por dicho comparador. Ambos operan con objetos.


const R = require('ramda');

// Creamos algunos objetos
let a = {
  nombre: "C",
  apellidos: "C",
  telefono: "9",
  direccion: "C"
};

let b = {
  nombre: "B",
  apellidos: "B",
  telefono: "1",
  direccion: "B"
};

let c = {
  nombre: "D",
  apellidos: "D",
  telefono: "6",
  direccion: "D"
};

let d = {
  nombre: "A",
  apellidos: "A",
  telefono: "4",
  direccion: "A"
};

// Los metemos en una lista
let lista = [a, b, c ,d];

// Creamos los comparadores
let porNombre = R.comparator((a,b) => a.nombre < b.nombre );
let porTelefono = R.comparator((a,b) => a.telefono < b.telefono );

// Ordenamos
let ordenadaNombre = R.sort(porNombre, lista);
let ordenadaTelefono = R.sort(porTelefono, lista);

// Mostramos
console.log(ordenadaNombre);
console.log(ordenadaTelefono);

ascend y descend

Al hilo de las ordenaciones tenemos otras dos funciones que permiten crear de forma rápida y sencilla comparadores ascendentes y descendentes.


let porDireccionAsc = R.ascend(R.prop('direccion'));
let porDireccionDesc = R.descend(R.prop('direccion'));

let ordenadaDireccionAsc = R.sort(porDireccionAsc, lista);
let ordenadaDireccionDesc = R.sort(porDireccionDesc, lista);

groupBy y countBy

A veces necesitamos agrupar bajo una clave nuestras colecciones de datos y tenemos que recurrir para dicha tarea a la base de datos. Con estas dos funciones podemos agrupar y contar elementos que cumplan con un determinado criterio.

Dada esta estructura de datos


// Creamos algunos objetos para su uso

let a = {
  nombre: "C",
  apellidos: "C",
  telefono: "9",
  direccion: "C",
  cp: '1000'
};

let b = {
  nombre: "B",
  apellidos: "B",
  telefono: "1",
  direccion: "B",
  cp: '1001'
};

let c = {
  nombre: "D",
  apellidos: "D",
  telefono: "6",
  direccion: "D",
  cp: '1000'
};

let d = {
  nombre: "A",
  apellidos: "A",
  telefono: "4",
  direccion: "A",
  cp: '1001'
};

Procedemos a crear un predicado que nos debe retornar una string que se usará como clave de agrupamiento.


// Preparamos el predicado para el groupBy
// Devolveremos el valor que usaremos como clave
const byCP = function(persona) {
  return persona.cp;
};

En nuestro caso devolveremos simplemente el código postal para que agrupe por dicho campo.


// Agrupamos. Devuelve un objeto con las claves devueltas en el predicado
// como campos cuyo valor es un array con los objetos que cumplna con dicho
// predicado
let resultadosGroupBy = R.groupBy(byCP,lista);

console.log(resultadosGroupBy);

// Tambien podemos contar cuantos elementos nos arroja el predicado
// con la funcion countBy
let resultadosCountBy = R.countBy(byCP, lista);

console.log(resultadosCountBy);

groupBy nos devolverá un objeto cuyos campos tendrán el nombre de las strings devueltas por el predicado y cuyo valor será un array con los objetos que complan con dicho criterio.
countBy nos devolverá un objeto con las citadas claves y con el valor de conteo de cada clave.

<h3>Resumen</h3>

Hemos visto una muestra de la funcionalidad de la librería Ramda.js. Muchas de sus funciones nos pueden ayudar en el dia a dia como programadores evitando tener que volver a reinventar la rueda implementando de forma elegante algoritmos que se suelen repetir en prácticamente todos los programas que podamos desarrollar. Podéis ver el código fuente de los ejemplos en mi repositorio de Github. En la proxima entrega veremos otra tanda de funciones útiles con ejemplos de cómo podemos integrarlas en nuestras aplicaciones.

Un saludo y hasta la próxima.