[Ramda] Lens in Depth

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);
复制代码

 

posted @   Zhentiw  阅读(706)  评论(0编辑  收藏  举报
编辑推荐:
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
历史上的今天:
2017-02-24 [SVG] Add an SVG as an Embedded Background Image
2017-02-24 [SVG] Add an SVG as a Background Image
2017-02-24 [SVG] Optimize SVGs for Better Performance using svgo
2017-02-24 [Angular] Dynamic components with ComponentFactoryResolver
2017-02-24 [Angular] Using the platform agnostic Renderer & ElementRef
2016-02-24 [Cycle.js] Hello World in Cycle.js
2016-02-24 [Cycle.js] From toy DOM Driver to real DOM Driver
点击右上角即可分享
微信分享提示