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

[Ramda] Lens in Depth的更多相关文章

  1. [Ramda] Getter and Setter in Ramda & lens

    Getter on Object: 1. prop: R.prop(}); //=> 100 R.prop('x', {}); //=> undefined 2. props: R.pro ...

  2. [Functional Programming] Using Lens to update nested object

    For example, in React application, we have initial state; const data = { nextId: 4, todoFilter: 'SHO ...

  3. [React] Update Component State in React With Ramda Lenses

    In this lesson, we'll refactor a React component to use Ramda lenses to update our component state. ...

  4. [Ramda] Change Object Properties with Ramda Lenses

    In this lesson we'll learn the basics of using lenses in Ramda and see how they enable you to focus ...

  5. Unity3D图像后处理特效——Depth of Field 3.4

    Depth of Field 3.4 is a common postprocessing effect that simulates the properties of a camera lens. ...

  6. [LeetCode] Minimum Depth of Binary Tree 二叉树的最小深度

    Given a binary tree, find its minimum depth. The minimum depth is the number of nodes along the shor ...

  7. [LeetCode] Maximum Depth of Binary Tree 二叉树的最大深度

    Given a binary tree, find its maximum depth. The maximum depth is the number of nodes along the long ...

  8. leetcode 111 minimum depth of binary tree

    problem description: Given a binary tree, find its minimum depth. The minimum depth is the number of ...

  9. 【leetcode】Minimum Depth of Binary Tree

    题目简述: Given a binary tree, find its minimum depth. The minimum depth is the number of nodes along th ...

随机推荐

  1. 063 SparkStream数据接收方式

    1.两种方式 2.Basic Source 由StreamingContext可以提供的API 上面做的wordcount中的方式就算是第一种方式. 3.Advanced Source 使用数据接收器 ...

  2. LoadRunner脚本参数化之设置条件与运行结果说明

    性能测试中为什么需要进行参数化? 1.功能方面:首先要保证脚本的功能完善.可用性.(一般来说,参数化主要针对业务中的具备唯一性的数据.) 2.性能方面:一般来说,如果服务器存在缓存机制,在测试过程中, ...

  3. CentOS 7 休眠系统

    CentOS 7的电源按钮只有关机和重启两项,但是可以用命令来休眠系统: 重启: $ systemctl reboot 退出系统并停止电源: $ systemctl poweroff 待机: $ sy ...

  4. POJ 3275 Ranking the Cows(传递闭包)【bitset优化Floyd】+【领接表优化Floyd】

    <题目链接> 题目大意:FJ想按照奶牛产奶的能力给她们排序.现在已知有N头奶牛$(1 ≤ N ≤ 1,000)$.FJ通过比较,已经知道了M$1 ≤ M ≤ 10,000$对相对关系.每一 ...

  5. HDU 3416 Marriage Match IV 【最短路】(记录路径)+【最大流】

    <题目链接> 题目大意: 给你一张图,问你其中没有边重合的最短路径有多少条. 解题分析: 建图的时候记得存一下链式后向边,方便寻找最短路径,然后用Dijkstra或者SPFA跑一遍最短路, ...

  6. Mysql数据库报错:Cannot add or update a child row: a foreign key constraint fails(添加多对多关系)

    #创建班级表 class Classes(models.Model): title = models.CharField(max_length=32) n=models.ManyToManyField ...

  7. 003.MMM双主-双从读写分离部署

    一 前期规划 1.1 主机规划 1.2 虚拟IP规划 1.3 用户列表 提示:以上角色需要在所有节点添加. 1.4 整体架构 1.4 hosts修改 1 [root@localhost ~]# vi ...

  8. Java基础中的RMI介绍与使用

    今天在这边介绍一下Java基础中的rmi使用.其实rmi有什么样的使用场景呢?它跟webservice有什么区别呢?其实webservice主要是可以跨语言实现项目间的方法调用,而rmi只是java内 ...

  9. WCF、WebAPI、WCFREST、WebService、WPF之间的区别

    在.net平台下,有大量的技术让你创建一个HTTP服务,像Web Service,WCF,现在又出了Web API.在.net平台下,你有很多的选择来构建一个HTTP Services.我分享一下我对 ...

  10. 366. Fibonacci

    描述 查找斐波纳契数列中第 N 个数. 所谓的斐波纳契数列是指: 前2个数是 0 和 1 . 第 i 个数是第 i-1 个数和第i-2 个数的和. 斐波纳契数列的前10个数字是: 0, 1, 1, 2 ...