Ramda js:用于具有嵌套对象数组的深层嵌套对象的镜头
Ramda js: lens for deeply nested objects with nested arrays of objects
使用Ramda.js(和镜头),我想修改下面的JavaScript对象,将ID=“/的对象的"NAME:VERSION1"更改为"NAME:VERSION2" 1/B/i".
我想使用镜头,因为我只想更改一个深度嵌套的值,但要保持整个结构不变。
我不想使用 lensIndex,因为我永远不知道数组的顺序是什么,所以我想 "find" 通过查找数组中的对象 "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": {}
}
}
}
]
}
]
}
这是一种解决方案:
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')))))));
当然,这可以分解成更小的功能。 :)
这应该可以通过创建一个通过 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);
使用Ramda.js(和镜头),我想修改下面的JavaScript对象,将ID=“/的对象的"NAME:VERSION1"更改为"NAME:VERSION2" 1/B/i".
我想使用镜头,因为我只想更改一个深度嵌套的值,但要保持整个结构不变。
我不想使用 lensIndex,因为我永远不知道数组的顺序是什么,所以我想 "find" 通过查找数组中的对象 "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": {}
}
}
}
]
}
]
}
这是一种解决方案:
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')))))));
当然,这可以分解成更小的功能。 :)
这应该可以通过创建一个通过 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);