功能编程:属性总和

时间:2016-04-01 08:30:50

标签: javascript functional-programming ramda.js

我正在尝试在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.composeR.pipe的某些组合,但我没有运气

3 个答案:

答案 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);