R.evolve
允许我们将对象属性替换为应用于该属性的当前值的函数的结果:
R.evolve({ count: R.inc }, { count: 1 })
== { count: 2 }
但我经常发现我想添加一个从输入对象的多个属性计算的属性:
assocFruitTotal({ appleCount: 5, orangeCount: 3 })
== { appleCount: 5, orangeCount: 3, fruitCount: 8 }
我提出了自己的简单实用功能:
const assocDerived = R.curry(
(name, f, obj) => ({
...obj,
[name]: f(obj)
});
......我经常使用它:
const sumFruit = R.pipe(
R.props(['appleCount', 'orangeCount']),
R.sum);
const assocFruitTotal = assocDerived('fruitCount', sumFruit);
但是我使用它的绝对频率让我想知道为什么它不是原生的Ramda,因为许多其他方便的功能。这让我想知道我是否缺少一个能够实现结果的更好的习语 - 也就是说,通过添加基于其他属性组合的属性来构建对象中的细节。
我应该使用的是惯用的函数式编程结构吗?
就个人而言,我会这样做:
const fruitCount = applySpec({fruitCount: compose(sum, values)})
fruitCount({apple: 5, orange: 3})
//=> {"fruitCount": 8}
const withFruitCount = converge(mergeRight, [identity, fruitCount]);
withFruitCount({apple: 5, orange: 3});
//=> {"apple": 5, "fruitCount": 8, "orange": 3}
如果要从总和中排除非计数属性,则可以使用pickBy
:
const pickCount = pickBy(flip(includes('Count')));
pickCount({appleCount: 5, orangeCount: 3, foo: 'bar'});
//=> {"appleCount": 5, "orangeCount": 3}
让我们首先认识到obj.value = f(obj)
是一个可变的赋值,因此不是一个功能性的习惯用语。这是工作中必不可少的思维方式。
在大多数情况下,将计算值存储为对象的属性是一个失误。如果appleCount
或orangeCount
发生变化,那么就没有任何东西可以强制执行fruitCount
的完整性。
fruitCount
应该是一个功能,而不是财产。
const fruitCount =
pipe
( props ([ 'appleCount', 'orangeCount' ])
, sum
)
fruitCount ({ appleCount: 1, orangeCount: 3 }) // 4
fruitCount ({ appleCount: 5, orangeCount: 3 }) // 8
如果我不得不猜测,这是假数据和示例问题。在某些情况下,计算值确实有意义(记忆是第一种想到的技术)但这些情况构成了例外,而不是规则。你说“我使用它的频率很高......”,所以我打赌你会在比你想要的更多的区域内做到这一点。
正如你所指出的那样,Ramda没有内置功能,所以这应该进一步表明有更多的传统方法可以解决这类问题。
面向对象的程序员会将其指定为计算属性 -
const FruitData = function (apples = 0, oranges = 0)
{ this.apples = apples
this.oranges = oranges
}
Object.defineProperty
( FruitData.prototype
, 'fruitCount'
, { get () { return this.apples + this.oranges } }
)
const f =
new FruitData (3, 4)
console .log (f.fruitCount) // 7
在编写功能风格时,我们将OOP概念留在门口。开始考虑功能,你的问题就消失了 -
const FruitData = (apples = 0, oranges = 0) =>
({ apples, oranges })
const appleCount = fd =>
fd.apples
const orangeCount = fd =>
fd.oranges
const fruitCount = fd =>
appleCount (fd) + orangeCount (fd)
console .log (fruitCount (FruitData (10, 3))) // 13