使用不同的镜头依次将多个功能应用于对象

时间:2019-11-12 09:38:45

标签: javascript functional-programming ramda.js 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

3 个答案:

答案 0 :(得分:3)

与OriDrori的converge解决方案一样,您也可以使用其他两个Ramda函数之一。我总是喜欢lift胜过nginx: [warn] the "user" directive makes sense only if the master process runs with super-user privileges, ignored in /etc/nginx/nginx.conf:1 nginx: [emerg] PEM_read_bio_X509_AUX("/etc/ssl/certs/surveyspan.crt") failed (SSL: error:0909006C:PEM routines:get_name:no start line:Expecting: TRUSTED CERTIFICATE) nginx: configuration file /etc/nginx/nginx.conf test failed 的情况;感觉更像是标准FP,其中converge非常像Ramda制品。由于converge的某些可变参数,它并不总是能胜任。但这确实在这里,您可以编写:

converge

但是,这两种解决方案中的identity都让我想知道是否还有更好的东西。还有。 Ramda的chain应用于函数时,有时也称为八哥组合器const calc = pipe ( over (arrLens, map (multiply (2))), lift (set (res0sumOfDblLens) ) ( pipe (view (arrLens), sum), identity ) ) 。或者说另一种方式,:: (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))

答案 1 :(得分:2)

要获取更新的值和对象,以便设置新的总和,可以使用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>

答案 2 :(得分:0)

也许没有镜头的变体更适合您的情况?

const doubleArr = pipe(
    path(['arr']),
    map(x => x*2)
  )
const newData = applySpec({
  arr: doubleArr,
  result: {
    sumOfDoubled: pipe(
       doubleArr,
       sum
    )
  }
})