Use With Objects

Lenses focus on a piece of a data structure. They're composable and provide great ways to read/update the focused piece. (5 min. read)

Hopefully the last few exercises challenged you. Now let’s sit back and soak in some more theory.

Lenses

Like the name implies, lenses let you “zoom in” on a particular piece of a data structure.

Getters

Let’s use good ol’ Bobo as an example.

import { lensProp, view } from 'ramda';
const person = {
firstName: 'Bobo',
lastName: 'Flakes'
};
const fNameLens = lensProp('firstName');
const result = view(fNameLens, person);
console.log({ result });

lensProp creates a lens focused on an object’s property. In this case, fNameLens will find any object’s firstName property. Passing it to view with our person returns Bobo’s first name.

Setters

Changing Bobo’s first name is just as easy. Use set with your desired change.

import { lensProp, set } from 'ramda';
const person = {
firstName: 'Bobo',
lastName: 'Flakes'
};
const fNameLens = lensProp('firstName');
const result = set(fNameLens, 'Bobo Jr.', person);
console.log({ person });
console.log({ result });

If you’d like to change it using a function, over can help.

import { concat, lensProp, over } from 'ramda';
const person = {
firstName: 'Bobo',
lastName: 'Flakes'
};
const fNameLens = lensProp('firstName');
const result = over(fNameLens, concat('Mr. '), person);
console.log({ person });
console.log({ result });

Note that set and over didn’t mutate the original person object. Ramda functions don’t mutate their inputs but instead return a new output.

Nested Properties

Lenses are great for safely changing nested properties without a ton of merging code.

Using plain JS, how would you immutably change Bobo’s manager’s last name to “Flakes”? Probably something like this

const person = {
firstName: 'Bobo',
lastName: 'Flakes',
company: 'Fake Inc.',
position: {
title: 'Front-End Developer',
department: {
name: 'Product',
departmentManager: {
firstName: 'Bobo Sr.',
lastName: 'Flax'
}
}
}
};
const correctPerson = {
...person,
position: {
...person.position,
department: {
...person.position.department,
departmentManager: {
...person.position.department.departmentManager,
lastName: 'Flakes'
}
}
}
};
const correctedLastName = correctPerson.position.department.departmentManager.lastName;
console.log({ correctedLastName });

Not too pretty, even with ES6 spread. Let’s try lenses.

import { lensPath, set, view } from 'ramda';
const person = {
firstName: 'Bobo',
lastName: 'Flakes',
company: 'Fake Inc.',
position: {
title: 'Front-End Developer',
department: {
name: 'Product',
departmentManager: {
firstName: 'Bobo Sr.',
lastName: 'Flax'
}
}
}
};
const managerLastNameLens = lensPath([
'position',
'department',
'departmentManager',
'lastName'
]);
const correctPerson = set(managerLastNameLens, 'Flakes', person);
const correctedLastName = view(managerLastNameLens, correctPerson);
console.log({ correctedLastName });

Much cleaner. One bonus is that since it’s curried, a single lens can be used to view and update a property. We used managerLastNameLens in both set and view.