[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()
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的更多相关文章
- [Ramda] Getter and Setter in Ramda & lens
Getter on Object: 1. prop: R.prop(}); //=> 100 R.prop('x', {}); //=> undefined 2. props: R.pro ...
- [Functional Programming] Using Lens to update nested object
For example, in React application, we have initial state; const data = { nextId: 4, todoFilter: 'SHO ...
- [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. ...
- [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 ...
- Unity3D图像后处理特效——Depth of Field 3.4
Depth of Field 3.4 is a common postprocessing effect that simulates the properties of a camera lens. ...
- [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 ...
- [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 ...
- leetcode 111 minimum depth of binary tree
problem description: Given a binary tree, find its minimum depth. The minimum depth is the number of ...
- 【leetcode】Minimum Depth of Binary Tree
题目简述: Given a binary tree, find its minimum depth. The minimum depth is the number of nodes along th ...
随机推荐
- AtCoder Grand Contest 017D (AGC017D) Game on Tree 博弈
原文链接https://www.cnblogs.com/zhouzhendong/p/AGC017D.html 题目传送门 - AGC017D 题意 给定一棵 n 个节点的以节点 1 为根的树. 两个 ...
- 2018牛客网暑假ACM多校训练赛(第四场)C Chiaki Sequence Reloaded (组合+计数) 或 数位dp
原文链接https://www.cnblogs.com/zhouzhendong/p/NowCoder-2018-Summer-Round4-C.html 题目传送门 - https://www.no ...
- 今天一起探讨shiro实现账户同一时刻session唯一
今天和同事在一起探讨shiro如何实现一个账户同一时刻只有一session存在的问题,下面小编把核心代码分享到博客园平台,需要的朋友参考下http://m.0834jl.com 今天遇到一个项目问题, ...
- iOS Runtime(一)、objc_class深深的误解
现在网上讲解的objc_class 绝大部分是错的.18年.19年依然很多童鞋写着错误的Runtime文章发到网上,面试的时候基本绝大部分人都说着网上所谓的"正确答案". 一.错误 ...
- Socket/ServerSocket 选项
在网络编程中,Socket/ServerSocket有一些选项用来自定义一些行为,现在分享一下. Socket选项 1.TCP_NODELAY 在Socket发送数据时,默认情况下,数据会先进 ...
- 同一台电脑上个人的github账户如何与公司的gitlab账户共存
前些天,写了篇博客 开发环境之git:团队协作git工作流与常用命令. 主要是回顾其中的第一小节基本配置. 但是对于很多程序员而言,我们不仅有公司的gitlab账户做公司的业务,也会有自己个人的git ...
- Linux学习笔记6
less 命令 空格或者 ctrl+f 前进一屏 ctrl+b 回退一屏 回车 前进一行 /String 查找含有string 字符串的页 ?String 同上 <反方向查找> ...
- 关于sql server的一种简单用法——在上面写查询语句,即可在下面修改数据
选择数据库中的表,右键单击-->编辑前200行,然后在显示的页面中选择带SQL标志的图标
- word表分页表头
选中表,右键-表格属性. 调出表格属性对话框. 选择行,在各页顶端以标题行形式重复出现划上钩确定.
- Java并发编程(十一)-- Java中的锁详解
上一章我们已经简要的介绍了Java中的一些锁,本章我们就详细的来说说这些锁. synchronized锁 synchronized锁是什么? synchronized是Java的一个关键字,它能够将代 ...