我正在尝试在JS using Ramda中实现一个函数,它接受一个对象列表并返回特定属性的总和。 E.g。
var l = [
{a: 1, b: 2, c: 0},
{a: 1, b: 3, c: -1},
{a: 1, b: 4, c: 0},
]
func(['a', 'b'], l)
-> {a: 3, b: 9}
原则上,我需要这样的功能:
R.map(R.props($1, _), $2)
在函数式编程中实现类似这样的东西最优雅的方法是什么? R.map(R.props)
由于显而易见的原因无效。我尝试使用R.compose
或R.pipe
的某些组合,但我没有运气
答案 0 :(得分:2)
我会将其分为两部分:
const fnOverProp = R.curry((fn, prop, list) => fn(R.pluck(prop, list)));
const fnOverProps = R.curry((fn, props, list) =>
R.fromPairs(R.zip(props)(R.map(fnOverProp(fn, __, list), props))));
(对不起,我在这里命名有一个创意块。这些名字非常糟糕。)
您可以像这样使用它:
fnOverProp(R.sum, 'b', list); //=> 9
fnOverProps(R.sum, ['a', 'b'], list); //=> {a: 3, b: 9}
const sumOverProps = fnOverProps(R.sum);
sumOverProps(['a', 'c'], list); //=> {a: 3, c: -1}
首先请注意,我将您的想法概括为sum
参数。我觉得这不是人们可能想要用这种功能做的唯一事情。
然后我将其分解为一个对单个属性名称进行操作的函数。这对我来说非常有用。您可能不需要为它们的完整列表执行此操作,并且此函数值得单独使用。
然后我将它包装在一个函数中,该函数将第一个函数映射到属性列表上。请注意,这实际上是一个非常简单的功能:
(fn, props, list) => R.map(fnOverProp(fn, R.__, list), props)
包装在两个包装器中,将结果的平面列表转换为您正在寻找的对象输出。 R.zip(props)(<thatFn>, props)
将[3, -1]
变为[['a', 3], ['c', -1]]
,然后R.fromPairs
将其变为{a: 3, c: -1}
。
这不会为您提供您想要的单行实现。显然你可以将第一个函数的定义折叠成第二个函数,但我不认为这会让你获益匪浅。即使它可以无点,我希望在这种情况下只会降低可读性。
您可以在 Ramda REPL 。
中查看此操作答案 1 :(得分:1)
这也可以定义为对象列表上的reducer。给定一些结果的初始状态,我们想要一个能够对两个对象属性的结果求和的函数,其中props
是我们感兴趣的属性列表:
reduce(useWith(mergeWith(add, [identity, pick(props)]))
然后,您有两个选项可以确定列表是非空的还是保证至少有一个对象。如果列表保证非空,则reducer的初始值可以简单地作为列表的头部,将尾部作为要迭代的列表。
const func = (props, objs) =>
reduce(useWith(mergeWith(add), [identity, pick(props)]), pick(props, head(objs)), tail(objs))
但是,如果列表可能为空,则必须使用空值初始化reduce
函数(在这种情况下为零)。
const func = (props, objs) =>
reduce(useWith(mergeWith(add), [identity, pick(props)]), pick(props, map(flip(objOf)(0), props)), objs)
答案 2 :(得分:0)
你可以试试这个:
function add(a, b){
return Object.keys(a).reduce(function(p, c){
p[c] += a[c];
return p;
}, b);
}
console.log(R.reduce(add, {a:0, b:0, c:0}, l))
这是另一种方法:
R.pipe(R.map(R.props(['a', 'b', 'c'])), R.transpose, R.map(R.sum))(l);