如何在给定的对象结构中使用 Ramda 执行嵌套更新?

How to perform a nested update using Ramda in the given object structure?

假设以下对象,如何使用 Ramda 在给定应用程序、条件 ID 和数据的条件中执行嵌套更新?

const application = {
  id: 'a1',
  features: [
    {
      id: 'f1',
      criterias: [
        { id: 'c1' }
      ]
    },
    {
      id: 'f2',
      criterias: [
        { id: 'c2' },
        { id: 'c3' }
      ]
    }
  ]
}

该函数看起来像这样:

const updateCriteria = (application, criteriaId, data) => // magic...

updateCriteria(application, 'c2', { name: 'foo' })

// output: {
//  id: 'a1',
//  features: [
//    {
//      id: 'f1',
//      criterias: [
//        { id: 'c1' }
//      ]
//    },
//    {
//      id: 'f2',
//      criterias: [
//        { id: 'c2', name: 'foo' },
//        { id: 'c3' }
//      ]
//    }
//  ]
// }

镜头可能是你最好的选择。 Ramda 有一个泛型 lens function, and specific ones for an object property (lensProp), for an array index(lensIndex), and for a deeper path(lensPath),但它不包括通过 id 在数组中查找匹配值的方法。不过,我们自己制作并不难。

通过将两个函数传递给 lens 来制作镜头:一个 getter 获取对象和 returns 相应的值,以及一个 setter 获取新值和对象以及 return 对象的更新版本。

这里我们写 lensMatch 来查找或设置数组中的值,其中给定的 属性 名称与提供的值相匹配。 lensId 只是将 'id' 传递给 lensMatch 以取回一个函数,该函数将采用 id 值和 return 镜头。

使用任何镜头,我们都有 view, set, and over 函数,分别获取、设置和更新值。

我们可以这样使用 idLens

const data = [{id: 'a'}, {id: 'b'}, {id: 'c'}]

view (idLens ('b'), data) 
  //=> {id: 'b'}
set  (idLens ('b'), 'foo', data) 
  //=> [ {id: 'a'}, 'foo', {id: 'c'} ]
over (idLens ('b'), merge ({name: 'foo'}), data)
  //=> [ {id: 'a'}, {id: 'b', name: 'foo}, {id: 'c'} ]

所以对于你的问题,我们可以这样写:

const lensMatch = (propName) => (key) => lens
  ( find ( propEq (propName, key) )
    , (val, arr, idx = findIndex (propEq (propName, key), arr)) =>
         update (idx > -1 ? idx : length (arr), val, arr)
    )

const lensId = lensMatch ('id')

const updateCriteria = (featureId, criteriaId, data, application) => over 
  ( compose 
      ( lensProp ('features')
      , lensId (featureId) 
      , lensProp ('criterias') 
      , lensId (criteriaId)
      )
    , merge (data)
    , application
    )

const application = {id: 'a1', features: [{id: 'f1', criterias: [{ id: 'c1' }]}, {id: 'f2', criterias: [{ id: 'c2' }, { id: 'c3' }]}]}

const newApp = updateCriteria ('f2', 'c2', {name: 'foo'}, application)

console.log(newApp)
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>
<script>
const {lens, find, propEq, findIndex, update, length, view, set, over, compose, lensProp, merge} = R
</script>

但这前提是你知道featureId。如果您需要同时找到 featureId 和具有内部 ID 的嵌套对象,您可以为此编写一个更复杂的镜头,但它会更重量级。


小提示:'criteria' 已经是复数,所以 'criterias' 是单数。单数是'criterion'.