我想使用Ramda的标准函数集编写一个函数,它给出一个字典和一个键,它将增加键的值。实施例
fn('foo', {}) // => {foo: 1}
fn('foo', {foo: 1}) // => {foo: 2}
我已经非常接近,但却错过了正确的咖喱方式。
我有一个方法,它接受一个键和一个对象并返回一个:
// count :: Any -> Number
var count = R.compose(R.inc, R.defaultTo(0))
// countProp :: String -> Object -> Number
var countProp = R.curry(R.compose(count, (R.prop(R.__))))
countProp('foo', {foo:1}) // 2
countProp('foo', {}) // 1
现在我想返回一个新的数据结构
// accum :: String -> Object -> Object
var accum = R.curry(function(key, obj){
return R.assoc(key, countProp(key, obj), obj)
})
accum('foo', {foo: 1}) // => {foo: 2}
但问题是,为了使这一点自由,我必须弄清楚如何在函数设置中获取值以正确的顺序进行咖喱。我究竟做错了什么?我应该以不同的方式设置此功能吗?我试图设置它,所以两个相关的函数都会先获取键,然后是对象,但我错过了一些东西。我应该为此考虑一个特定的Functor吗?
谢谢!
答案 0 :(得分:5)
有几点:
首先,如果@ davidchambers'解决方案做你需要的,这很棒。当下一个版本的Ramda发布并添加lensProp时,它会更好,这将只是
var fooLens = R.lensProp('foo');
fooLens.map(R.inc, {foo: 1, bar: 2}); // => {foo: 2, bar: 2}
第二,原始功能和镜头版本之间存在差异:
accum('foo', {bar: 1}); //=> {"bar":1,"foo":1}
fooLens.map(R.inc, {bar: 1}); //=> {"bar":1,"foo":null}
第三,无论如何,如果你有兴趣确定如何以无点的方式包装你的功能,Ramda有几个功能可以提供帮助。有一个辅助函数nthArg
除了返回一个返回函数的函数之外什么都没做,只返回它调用的外部函数的第n个参数。然后有几个函数充当compose
的扩展版本,包括useWith
和converge
。
您可以像这样使用它们:
var accum = R.converge(R.assoc, R.nthArg(0), countProp, R.nthArg(1));
accum('foo', {foo: 1, bar: 2}); // => {foo: 2, bar: 2}
accum('foo', {bar: 2}); // => {foo: 1, bar: 2}
在此代码中,converge
将参数(key
和obj
)传递给作为参数传递的每个函数,除了第一个函数,然后传递每个函数的结果那些第一个功能。
最后,虽然这显示了一种免费编写此代码的方法,但它最终并不太可怕,但它可能不如您的早期版本清晰这不是免费的。我喜欢无点代码。但有时候我们会迷恋它,没有充分的理由让代码点免费。如果你最终不能使用lens
版本,你可能需要仔细考虑无点解决方案是否比替代解决方案更清晰。
答案 1 :(得分:4)
您可以使用R.lens
:
const fooLens = R.lens(R.prop('foo'), R.assoc('foo'));
fooLens.map(R.inc, {foo: 1, bar: 2}); // => {foo: 2, bar: 2}
fooLens.map(R.inc, {foo: 2, bar: 2}); // => {foo: 3, bar: 2}
fooLens.map(R.inc, {foo: 3, bar: 2}); // => {foo: 4, bar: 2}
透镜可以创建一系列值,而不会通过改变它来破坏成功值的完整性。