[Ramda] Lens in Depth
in depth Ramda
2023-09-14 09:00:49 时间
In this post, we are going to see how to use Ramda Lens.
For example, we have data:
const {log} = require('./lib/log'); const R = require('ramda'); const band = { name: 'K.M.F.D.M', members: { current: [ {name: 'Sascha Konictzko', plays: ['vocals', 'synth', 'guitar', 'bass']}, {name: 'Lucia Cifarelli', plays: ['vocals', 'synth']}, {name: 'Jules Hodgson', plays: ['guitar', 'bass', 'synth']}, {name: 'Steve While', plays: ['guitar']} ], past: [ {name: 'Raymond Watts', plays: ['vocals', 'synth']}, {name: 'En Esch', plays: ['vocals', 'drums', 'guitar', 'synth']}, {name: 'Gunter', plays: ['guitar', 'synth']} ] } };
R.lens:
R.lens takes a getter and a setter:
const name = R.lens(R.prop('name'), R.assoc('name')); log(R.view(name, band)); // K.M.F.D.M log(R.set(name, 'M.D.F.M.K', band));
R.lensProp:
There is a shorter way to do getter/setter partten:
const name = R.lensProp('name'); const makeLower = a => a.toLowerCase(); log('over lensProp: ', R.over(name, makeLower, band)); // name: "k.m.f.d.m"
log('over compose: ', R.compose(R.view(name), R.over(name, makeLower))(band)); // name: "k.m.f.d.m"
R.lensPath:
const currentMemebers = R.lensPath(['members', 'current']); log('view path', R.view(currentMemebers, band))
We can also combine two lens together:
const name = R.lensProp('name'); const currentMemebers = R.lensPath(['members', 'current']); //log('view path', R.view(currentMemebers, band)) const makeLower = a => a.toLowerCase(); // Use two lens together const lowerName = R.over(name, makeLower); const lowerMembers = R.over(currentMemebers, R.map(lowerName)); console.log('new lower name', lowerMembers(band));
R.lensIndex:
const first = R.lensIndex(0) const getFistCurrentMember = R.compose( R.view(first), R.view(currentMemebers) ) console.log(getFistCurrentMember(band)) // { name: 'Sascha Konictzko', plays: [ 'vocals', 'synth', 'guitar', 'bass' ] }
Actually there is a better way to do this: by using R.compose(), one thing to notice is that, when working with lens, compose should go from left to right, instead of right to left. This is because how lens works in code.
In our example:
const band = { name: 'K.M.F.D.M', members: { current: [ {name: 'Sascha Konictzko', plays: ['vocals', 'synth', 'guitar', 'bass']}, {name: 'Lucia Cifarelli', plays: ['vocals', 'synth']}, {name: 'Jules Hodgson', plays: ['guitar', 'bass', 'synth']}, {name: 'Steve While', plays: ['guitar']} ], past: [ {name: 'Raymond Watts', plays: ['vocals', 'synth']}, {name: 'En Esch', plays: ['vocals', 'drums', 'guitar', 'synth']}, {name: 'Gunter', plays: ['guitar', 'synth']} ] } };
We should get 'members' first then 'current' first member:
const currentMemebers = R.lensPath(['members', 'current']); const first = R.lensIndex(0) const firstCurrentMember = R.compose( currentMemebers, first ) console.log(R.view(firstCurrentMember, band))
You can think this is similar to Javascript Stack when using R.compose(lens1, len2).
Example:
Using the same data from previous example:
const band = { name: 'K.M.F.D.M', members: { current: [ {name: 'Sascha Konictzko', plays: ['vocals', 'synth', 'guitar', 'bass']}, {name: 'Lucia Cifarelli', plays: ['vocals', 'synth']}, {name: 'Jules Hodgson', plays: ['guitar', 'bass', 'synth']}, {name: 'Steve While', plays: ['guitar']} ], past: [ {name: 'Raymond Watts', plays: ['vocals', 'synth']}, {name: 'En Esch', plays: ['vocals', 'drums', 'guitar', 'synth']}, {name: 'Gunter', plays: ['guitar', 'synth']} ] } };
Requirements:
/** * 1. Lowercase the name * 2. Omit the plays from the past * 3. Lowercase the current name * 4. Create 'all' under members combine 'current' & 'past' * 5. Pick get name and to lowercase for 'members.all' */
Final results should be:
/** * { "name": "k.m.f.d.m", "members": { "current": [ { "name": "sascha konictzko", "plays": [ "vocals", "synth", "guitar", "bass" ] }, { "name": "lucia cifarelli", "plays": [ "vocals", "synth" ] }, { "name": "jules hodgson", "plays": [ "guitar", "bass", "synth" ] }, { "name": "steve while", "plays": [ "guitar" ] } ], "past": [ { "name": "Raymond Watts" }, { "name": "En Esch" }, { "name": "Gunter" } ], "all": [ "sascha konictzko", "lucia cifarelli", "jules hodgson", "steve while", "raymond watts", "en esch", "gunter" ] } } */
-----
Answer:
const R = require('ramda'); const band = { name: 'K.M.F.D.M', members: { current: [ {name: 'Sascha Konictzko', plays: ['vocals', 'synth', 'guitar', 'bass']}, {name: 'Lucia Cifarelli', plays: ['vocals', 'synth']}, {name: 'Jules Hodgson', plays: ['guitar', 'bass', 'synth']}, {name: 'Steve While', plays: ['guitar']} ], past: [ {name: 'Raymond Watts', plays: ['vocals', 'synth']}, {name: 'En Esch', plays: ['vocals', 'drums', 'guitar', 'synth']}, {name: 'Gunter', plays: ['guitar', 'synth']} ] } }; /** * 1. Lowercase the name * 2. Omit the plays from the past * 3. Lowercase the current name */ // makeLower :: s -> s const makeLower = s => s.toLowerCase(); // lowerName :: obj a -> obj b const lowerName = R.compose(makeLower, R.prop('name')); const name = R.lensProp('name'); const pastMemebers = R.lensPath(['members', 'past']); const currentMemebers = R.lensPath(['members', 'current']); // Mapping over NAME lens, make string to lowercase const lowerBandName = R.over( name, makeLower ); // Mapping over 'memebers.past' Lens, omit 'plays' prop const omitPastPlays = R.over( pastMemebers, R.map(R.omit(['plays'])) ); // Mapping over 'members.current' Lens, for each 'name' prop, make string to lowercase const lowerCurrentMemebersName = R.over( currentMemebers, R.map( R.over(name, makeLower) ) ); /** * 4. Create 'all' under members combine 'current' & 'past' * 5. Pick get name and to lowercase for 'members.all' */ const allMemebers = R.lensPath(['members', 'all']); // lensConcat :: (Lens t, Lens s) -> a -> [b] const lensConcat = (targetLen, srcLen) => data => R.over(targetLen, R.concat(R.view(srcLen, data)))(data); // A set of tranforms over prop 'members.all' // Set 'all' to [] // concat 'past' to 'all' // concat 'current' to 'all' // pick name and conver to lowercase const createAllMembers = [ R.over(allMemebers, R.map(lowerName)), lensConcat(allMemebers, currentMemebers), lensConcat(allMemebers, pastMemebers), R.set(allMemebers, []) ]; const mods = [ ...createAllMembers, lowerCurrentMemebersName, omitPastPlays, lowerBandName, ] const transform = R.compose( ...mods ); const res = transform(band);
相关文章
- ERROR [org.apache.hadoop.util.Shell] - Failed to locate the winutils binary in the hadoop binary path
- configure: error: no acceptable C compiler found in $PATH 问题解决
- [Ramda] Lens in Depth
- [Algorithm] Write a Depth First Search Algorithm for Graphs in JavaScript
- [HTML] Creating visual skip links in HTML and CSS
- [Javascript] JavaScript Array Methods in Depth - push
- [Javascript] Array methods in depth - some
- [Javascript] Object.freeze: using Object.freeze in function params to enforce immutabtility
- [Javascript] Private class properties in Javascript
- [Angular] How to get Store state in ngrx Effect
- [TypeScript] Sharing Class Behavior with Inheritance in TypeScript
- [Javascript] JavaScript Array Methods in Depth - push
- [Javascript] Array methods in depth - indexOf
- [Javascript] Hoisting in JavaScript
- Shell openSomething - how is application component loaded in the runtime
- BSP tag in CRM and JSP tag in Hybris
- another delta upload in CRM side
- Unable to install breakpoint in Modify compiler options to generate line number attributes
- escape in ABAP and JavaScript
- hdu1506——Largest Rectangle in a Histogram
- $digest already in progress 解决办法——续