Ramda js: lens for deeply nested objects with nested arrays of objects

后端 未结 2 1435
慢半拍i
慢半拍i 2021-02-01 06:11

Using Ramda.js (and lenses), I want to modify the JavaScript object below to change \"NAME:VERSION1\" to \"NAME:VERSION2\" for the object that has ID= \"/1/B/i\".

I want

相关标签:
2条回答
  • 2021-02-01 06:31

    This should be possible by creating a lens that matches an object by ID which can then be composed with other lenses to drill down to the image field.

    To start with, we can create a lens that will focus on an element of an array that matches some predicate (note: this will only be a valid lens if it is guaranteed to match at least one element of the list)

    //:: (a -> Boolean) -> Lens [a] a
    const lensMatching = pred => (toF => entities => {
        const index = R.findIndex(pred, entities);
        return R.map(entity => R.update(index, entity, entities),
                     toF(entities[index]));
    });
    

    Note that we're manually constructing the lens here rather than using R.lens to save duplication of finding the index of the item that matches the predicate.

    Once we have this function we can construct a lens that matches a given ID.

    //:: String -> Lens [{ id: String }] { id: String }
    const lensById = R.compose(lensMatching, R.propEq('id'))
    

    And then we can compose all the lenses together to target the image field

    const imageLens = R.compose(
      R.lensProp('groups'),
      lensById('/1/B'),
      R.lensProp('apps'),
      lensById('/1/B/i'),
      R.lensPath(['container', 'docker', 'image'])
    )
    

    Which can be used to update the data object like so:

    set(imageLens, 'NAME:VERSION2', data)
    

    You could then take this a step further if you wanted to and declare a lens that focuses on the version of the image string.

    const vLens = R.lens(
      R.compose(R.nth(1), R.split(':')),
      (version, str) => R.replace(/:.*/, ':' + version, str)
    )
    
    set(vLens, 'v2', 'NAME:v1') // 'NAME:v2'
    

    This could then be appended to the composition of imageLens to target the version within the entire object.

    const verLens = compose(imageLens, vLens);
    set(verLens, 'VERSION2', data);
    
    0 讨论(0)
  • 2021-02-01 06:33

    Here's one solution:

    const updateDockerImageName =
    R.over(R.lensProp('groups'),
           R.map(R.over(R.lensProp('apps'),
                        R.map(R.when(R.propEq('id', '/1/B/i'),
                                     R.over(R.lensPath(['container', 'docker', 'image']),
                                            R.replace(/^NAME:VERSION1$/, 'NAME:VERSION2')))))));
    

    This could be decomposed into smaller functions, of course. :)

    0 讨论(0)
提交回复
热议问题