使用不同的镜头依次将多个功能应用于对象
Sequentially apply multiple functions to object using different lenses
我想对对象中的数组执行一些更新,然后根据此更新计算另一个参数。这是我试过的:
import * as R from 'ramda'
const obj = {
arr: [
2,
3
],
result: {
sumOfDoubled: 0
}
};
const double = a => {
return a*2;
}
const arrLens = R.lensProp('arr');
const res0sumOfDblLens = R.lensPath(['result','sumOfDoubled']);
const calc = R.pipe(
R.over(arrLens,R.map(double)),
R.view(arrLens),
R.sum,
R.set(res0sumOfDblLens)
);
const updatedObjA = calc(obj);
const updatedObjB = R.set(res0sumOfDblLens,R.sum(R.view(arrLens,R.over(arrLens,R.map(double),obj))),obj);
// what I want: {"arr":[4,6],"result":{"sumOfDoubled":10}}
console.log(JSON.stringify(obj)); //{"arr":[2,3],"result":{"sumOfDoubled":0}}, as expected
console.log(JSON.stringify(updatedObjA)); //undefined
console.log(JSON.stringify(updatedObjB)); //{"arr":[2,3],"result":{"sumOfDoubled":10}}, correct result but the array did not update
我意识到这两种方法都行不通;方法 A 归结为 R.set(res0sumOfDblLens,10)
,这没有意义,因为它没有操作的目标对象。另一方面,方法 B 对基础对象进行两次操作,而不是将第一次操作的结果作为第二次操作的输入传递。
我怎样才能只使用一个函数组合来实现这个?即,将 double()
函数应用于对象的一部分,然后将更新后的对象作为输入传递给计算 sumOfDoubled
?
要获取更新后的值,和对象,所以你可以设置新的总和,你可以使用R.converge()
:
const arrLens = R.lensProp('arr');
const res0sumOfDblLens = R.lensPath(['result', 'sumOfDoubled']);
const calc = R.pipe(
R.over(arrLens, R.map(R.multiply(2))),
R.converge(R.set(res0sumOfDblLens), [
R.pipe(R.view(arrLens), R.sum),
R.identity
])
);
const obj = { arr: [2, 3], result: { sumOfDoubled: 0 }};
const result = calc(obj);
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>
以及 OriDrori 的 converge
solution, you could also use either of two other Ramda functions. I always prefer lift
到 converge
当它工作时;它感觉更像是标准的 FP,其中 converge
非常像是 Ramda 的神器。由于 converge
的一些可变特性,它并不总是能完成这项工作。但它在这里,你可以写:
const calc = pipe (
over (arrLens, map (multiply (2))),
lift (set (res0sumOfDblLens) ) (
pipe (view (arrLens), sum),
identity
)
)
但是 identity
in either of these solutions makes me wonder if there's something better. And there is. Ramda's chain
应用于函数时有时被称为 Starling 组合器 :: (a -> b -> c) -> (a -> b) -> a -> c
。或者换一种说法,chain (f, g) //~> (x) => f (g (x)) (x)
。这正是我们想要在这里应用的。所以有了 chain
,这进一步简化了:
const arrLens = lensProp('arr')
const res0sumOfDblLens = lensPath(['result', 'sumOfDoubled'])
const calc = pipe (
over (arrLens, map (multiply (2))),
chain (
set (res0sumOfDblLens),
pipe (view (arrLens), sum)
)
)
const obj = { arr: [2, 3], result: { sumOfDoubled: 0 }}
console .log (calc (obj))
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>
<script>const {lensProp, lensPath, pipe, over, map, multiply, chain, set, view, sum} = R </script>
也许没有镜头的变体更适合您的情况?
const doubleArr = pipe(
path(['arr']),
map(x => x*2)
)
const newData = applySpec({
arr: doubleArr,
result: {
sumOfDoubled: pipe(
doubleArr,
sum
)
}
})
我想对对象中的数组执行一些更新,然后根据此更新计算另一个参数。这是我试过的:
import * as R from 'ramda'
const obj = {
arr: [
2,
3
],
result: {
sumOfDoubled: 0
}
};
const double = a => {
return a*2;
}
const arrLens = R.lensProp('arr');
const res0sumOfDblLens = R.lensPath(['result','sumOfDoubled']);
const calc = R.pipe(
R.over(arrLens,R.map(double)),
R.view(arrLens),
R.sum,
R.set(res0sumOfDblLens)
);
const updatedObjA = calc(obj);
const updatedObjB = R.set(res0sumOfDblLens,R.sum(R.view(arrLens,R.over(arrLens,R.map(double),obj))),obj);
// what I want: {"arr":[4,6],"result":{"sumOfDoubled":10}}
console.log(JSON.stringify(obj)); //{"arr":[2,3],"result":{"sumOfDoubled":0}}, as expected
console.log(JSON.stringify(updatedObjA)); //undefined
console.log(JSON.stringify(updatedObjB)); //{"arr":[2,3],"result":{"sumOfDoubled":10}}, correct result but the array did not update
我意识到这两种方法都行不通;方法 A 归结为 R.set(res0sumOfDblLens,10)
,这没有意义,因为它没有操作的目标对象。另一方面,方法 B 对基础对象进行两次操作,而不是将第一次操作的结果作为第二次操作的输入传递。
我怎样才能只使用一个函数组合来实现这个?即,将 double()
函数应用于对象的一部分,然后将更新后的对象作为输入传递给计算 sumOfDoubled
?
要获取更新后的值,和对象,所以你可以设置新的总和,你可以使用R.converge()
:
const arrLens = R.lensProp('arr');
const res0sumOfDblLens = R.lensPath(['result', 'sumOfDoubled']);
const calc = R.pipe(
R.over(arrLens, R.map(R.multiply(2))),
R.converge(R.set(res0sumOfDblLens), [
R.pipe(R.view(arrLens), R.sum),
R.identity
])
);
const obj = { arr: [2, 3], result: { sumOfDoubled: 0 }};
const result = calc(obj);
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>
以及 OriDrori 的 converge
solution, you could also use either of two other Ramda functions. I always prefer lift
到 converge
当它工作时;它感觉更像是标准的 FP,其中 converge
非常像是 Ramda 的神器。由于 converge
的一些可变特性,它并不总是能完成这项工作。但它在这里,你可以写:
const calc = pipe (
over (arrLens, map (multiply (2))),
lift (set (res0sumOfDblLens) ) (
pipe (view (arrLens), sum),
identity
)
)
但是 identity
in either of these solutions makes me wonder if there's something better. And there is. Ramda's chain
应用于函数时有时被称为 Starling 组合器 :: (a -> b -> c) -> (a -> b) -> a -> c
。或者换一种说法,chain (f, g) //~> (x) => f (g (x)) (x)
。这正是我们想要在这里应用的。所以有了 chain
,这进一步简化了:
const arrLens = lensProp('arr')
const res0sumOfDblLens = lensPath(['result', 'sumOfDoubled'])
const calc = pipe (
over (arrLens, map (multiply (2))),
chain (
set (res0sumOfDblLens),
pipe (view (arrLens), sum)
)
)
const obj = { arr: [2, 3], result: { sumOfDoubled: 0 }}
console .log (calc (obj))
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>
<script>const {lensProp, lensPath, pipe, over, map, multiply, chain, set, view, sum} = R </script>
也许没有镜头的变体更适合您的情况?
const doubleArr = pipe(
path(['arr']),
map(x => x*2)
)
const newData = applySpec({
arr: doubleArr,
result: {
sumOfDoubled: pipe(
doubleArr,
sum
)
}
})