[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);

 

转载于:https://www.cnblogs.com/Answer1215/p/10425203.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值