函数式编程:属性总和
Functional Programming: Sum of properties
我正在尝试在 JS using Ramda 中实现一个函数,该函数接受对象列表和 returns 特定属性的总和。例如
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(, _), )
在函数式编程中实现这样的东西最优雅的方法是什么? R.map(R.props)
由于显而易见的原因不起作用。我尝试使用 R.compose
或 R.pipe
的一些组合,但我没有运气
你可以试试这个:
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);
我会把它分成两部分:
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.
中看到实际效果
这也可以定义为对象列表的缩减器。给定结果的一些初始状态,我们想要一个可以对两个对象属性的结果求和的函数,其中 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)
我正在尝试在 JS using Ramda 中实现一个函数,该函数接受对象列表和 returns 特定属性的总和。例如
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(, _), )
在函数式编程中实现这样的东西最优雅的方法是什么? R.map(R.props)
由于显而易见的原因不起作用。我尝试使用 R.compose
或 R.pipe
的一些组合,但我没有运气
你可以试试这个:
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);
我会把它分成两部分:
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.
中看到实际效果这也可以定义为对象列表的缩减器。给定结果的一些初始状态,我们想要一个可以对两个对象属性的结果求和的函数,其中 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)