Handling objects with Ramda
Tags: javascript, ramda, functional
Published: 2019-11-10 18:09:28

*I recently started learning functional programming and found this awesome library: Ramda. In this post I will show how easy handling objects is with Ramda.*

# What is Ramda anyway?

Ramda is a functional programming library that emphasises pure function style, immutability and side-effect free functions. It is build of a bunch of small functions that can work effective together. 

All these functions are automatically curried, it other words they can called with less argument than they expect. In that case they return a function that holds the argument already passed and expects the rest.

# prop, propOr

Let's say we want to log the maths grade of each student or log 'No grade' if they don't have maths grade.

```javascript
import * as R from 'ramda';

const student1 = {
  name: 'Alice',
  grades: {
    english: 'B',
    history: 'C',
    biology: 'D',
  },
};

const student2 = {
  name: 'Bob',
  grades: {
    maths: 'A',
    english: 'B',
    history: 'C',
  },
};
const student3 = {
  name: 'Cecile',
};

const students = [student1, student2, student3];
students.forEach(student => {
  const grade =
    student.grades && student.grades.maths ? student.grades.maths : 'No grade';
  console.log(`${student.name} \t ${grade}`);
});

// Alice    No grade
// Bob      A
// Cecile   No grade
```
I can't say it is easy to read and if we had more deeply nested objects the code would become very ugly very quickly. With Ramda the same code would look like this.

```javascript
import * as R from 'ramda';

const gimmeTheGrades = R.prop('grades'); 

students.forEach(student => {
  const grade = R.propOr('No grade', 'maths', gimmeTheGrades(student));
  console.log(`${student.name} \t ${grade}`);
});

// Alice    No grade
// Bob      A
// Cecile   No grade
```

_gimmeTheGrades_ is a function that returns the _grades_ property of the object I pass to it, if no _grades_ then it simply returns undefined. _propOr_ takes an extra argument - default value. If the result is falsey, it returns the default value. 

If I needed a grade with default value somewhere else later in my app, I would have done this. 

```javascript
import * as R from 'ramda';

const gimmeTheGrades = R.prop('grades');
const gradeWithDefault = R.propOr('No grade');

students.forEach(student => {
  const grade = gradeWithDefault('maths', gimmeTheGrades(student));
  console.log(`${student.name} \t ${grade}`);
});

// Alice    No grade
// Bob      A
// Cecile   No grade
```

# path, pathOr

What if we need a value of a deeply nested property? We can use _path_ or _pathOr_. It works the same as _prop_ and _propOr_, but takes an array of stings, instead of one single string.

```javascript
import * as R from 'ramda';

const gradeWithDefault = R.pathOr('No grade');
const gimmeTheMathGrade = gradeWithDefault(['grades', 'maths']);

students.forEach(student => {
  const grade = gimmeTheMathGrade(student);
  console.log(`${student.name} \t ${grade}`);
});

// Alice    No grade
// Bob      A
// Cecile   No grade
```

# getter/setter

It is very easy to define getter and setter functions for a property with _lens_ function. The first argument a function that gets the property, the second is the one that sets it. The setter should not change the data structure.


```javascript
import * as R from 'ramda';

const gradeWithDefault = R.pathOr('No grade');
const mathsLens = R.lens(
  gradeWithDefault(['grades', 'maths']),
  R.assocPath(['grades', 'maths']),
);

console.log(R.view(mathsLens, student1)); // No grade
console.log(R.view(mathsLens, student2)); // A

const newStudent1 = R.set(mathsLens, 'F', student1);
const newStudent2 = R.set(mathsLens, undefined, student2);

console.log(R.view(mathsLens, newStudent1)); // F
console.log(R.view(mathsLens, newStudent2)); // No grade

console.log(newStudent2);
// {
//   name: 'Bob',
//   grades: { maths: undefined, english: 'B', history: 'C' }
// }
```

Note: new Student has grades.maths property, but it is undefined.

# objOf, mergeLeft

_objOf_ creates an object with a single key:value pair, which can be merged with another object, so if we want to create an array with student objects we can do it like below.

```javascript
import * as R from 'ramda';

const names = ['Alice', 'Bob', 'Cecile'];
const defaultStudent = {
  grades: {
    english: null,
    history: null,
    biology: null,
  },
};

const createSudents = R.pipe(
  R.map(R.objOf('name')),
  R.map(R.mergeLeft(defaultStudent)),
);

const students = createSudents(names);

console.log(students);
```

_pipe_ takes functions as arguments and call them in order, passing the result of each function to the next. First we map through the names and create objects with a _name_ property.

```
[ { name: 'Alice' }, { name: 'Bob' }, { name: 'Cecile' } ]
```

Then we feed this to the next map and merge each one with _defaultGrades_.

```
[
  {
    name: 'Alice',
    grades: { english: null, history: null, biology: null }
  },
  {
    name: 'Bob',
    grades: { english: null, history: null, biology: null }
  },
  {
    name: 'Cecile',
    grades: { english: null, history: null, biology: null }
  }
]
```

Thanks for reading it. Happy coding. ❤

Say hello. 
[LinkedIn](https://www.linkedin.com/in/szib77/) | [Github](https://githib.com/szib) | [Instagram](https://www.instagram.com/szib77/)