繁体   English   中英

Ramda js:用于具有嵌套对象数组的深度嵌套对象的镜头

[英]Ramda js: lens for deeply nested objects with nested arrays of objects

使用 Ramda.js(和镜头),我想修改下面的 JavaScript 对象,将 ID=“/1/B/i”的对象的“NAME:VERSION1”更改为“NAME:VERSION2”。

我想使用一个镜头,因为我只想改变一个深度嵌套的值,否则保持整个结构不变。

我不想使用lensIndex,因为我永远不知道数组的顺序,所以相反,我想通过查找其“id”字段来“找到”数组中的对象。

我可以用镜头来做到这一点,还是应该换一种方式?

{
  "id": "/1",
  "groups": [
    {
      "id": "/1/A",
      "apps": [
        {
          "id": "/1/A/i",
          "more nested data skipped to simplify the example": {} 
        }
      ]
    },
    {
      "id": "/1/B",
      "apps": [
        { "id": "/1/B/n", "container": {} },
        {
          "id": "/1/B/i",

          "container": {
            "docker": {
              "image": "NAME:VERSION1",
              "otherStuff": {}
            }
          }
        }
      ]
    }

  ]
}

这应该可以通过创建一个通过 ID 匹配对象的镜头来实现,然后可以与其他镜头组合以深入到图像场。

首先,我们可以创建一个镜头,该镜头将重点放在匹配某个谓词的数组元素上(注意:只有保证与列表中的至少一个元素匹配,这才是有效镜头)

//:: (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]));
});

请注意,我们在这里手动构建镜头,而不是使用R.lens来避免重复查找与谓词匹配的项目的索引。

一旦我们有了这个函数,我们就可以构建一个匹配给定 ID 的镜头。

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

然后我们可以将所有镜头组合在一起以瞄准像场

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

可用于更新data对象,如下所示:

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

如果您想声明一个专注于图像字符串版本的镜头,您可以更进一步。

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

set(vLens, 'v2', 'NAME:v1') // 'NAME:v2'

然后可以将其附加到imageLens的组合中,以针对整个对象中的版本。

const verLens = compose(imageLens, vLens);
set(verLens, 'VERSION2', data);

这是一种解决方案:

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')))))));

当然,这可以分解为更小的功能。 :)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM