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

江枫思渺然 提交于 2019-12-22 11:27:45

问题


Assuming the follow object how is it possible to use Ramda to perform a nested update in a criteria given an application, criteria ID and data?

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

The function would look something like this:

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' }
//      ]
//    }
//  ]
// }

回答1:


Lenses are probably your best bet for this. Ramda has a generic lens function, and specific ones for an object property (lensProp), for an array index(lensIndex), and for a deeper path(lensPath), but it does not include one to find a matching value in an array by id. It's not hard to make our own, though.

A lens is made by passing two functions to lens: a getter which takes the object and returns the corresponding value, and a setter which takes the new value and the object and returns an updated version of the object.

Here we write lensMatch which find or sets the value in the array where a given property name matches the supplied value. And lensId simply passes 'id' to lensMatch to get back a function which will take an id value and return a lens.

Using any lens, we have the view, set, and over functions which, respectively, get, set, and update the value.

We could use idLens like this:

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'} ]

So for your problem, we could write something like this:

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>

But this presupposes that you know the featureId. If you need to find both the featureId and the nested object with your internal id, you could write a more complex lens for that, but it will be much more heavyweight.


A minor note: 'criteria' is already plural, so 'criterias' is odd. The singular is 'criterion'.



来源:https://stackoverflow.com/questions/54805737/how-to-perform-a-nested-update-using-ramda-in-the-given-object-structure

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!