首页 > 解决方案 > 如何在给定的对象结构中使用 Ramda 执行嵌套更新?

问题描述

假设跟随对象如何使用 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' }
//      ]
//    }
//  ]
// }

标签: javascriptfunctional-programmingramda.js

解决方案


镜头可能是你最好的选择。Ramda 有一个通用lens函数,以及针对对象属性 ( lensProp)、数组索引 ( lensIndex) 和更深路径 ( lensPath) 的特定函数,但它不包括通过 id 在数组中查找匹配值的函数。不过,自己制作并不难。

镜头是通过将两个函数传递给lens:一个获取对象并返回相应值的获取器,以及一个获取新值和对象并返回对象的更新版本的设置器。

在这里,我们编写lensMatchwhich 在给定属性名称与提供的值匹配的数组中查找或设置值。并lensId简单地传递'id'给以lensMatch取回一个函数,该函数将获取一个 id 值并返回一个镜头。

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

我们可以这样使用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 找到 the 和嵌套对象,您可以为此编写一个更复杂的镜头,但它会更重量级。


一个小提示:'criteria' 已经是复数了,所以'criterias' 是奇怪的。单数是“标准”。


推荐阅读