Ramda js: объектив для глубоко вложенных объектов с вложенными массивами объектов
Использование Ramda.js (и линзы), я хочу изменить объект JavaScript ниже, чтобы изменить "NAME:VERSION1" на "NAME:VERSION2" для объекта, который имеет ID= "/1/B/i".
Я хочу использовать объектив, потому что я хочу просто изменить одно глубоко вложенное значение, но в противном случае сохранить всю структуру без изменений.
Я не хочу использовать 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": {}
}
}
}
]
}
]
}
2 ответов
это должно быть возможно путем создания объектива, который соответствует объекту по идентификатору, который затем может быть составлен с другими объективами для детализации до поля изображения.
для начала мы можем создать объектив, который будет фокусироваться на элементе массива, который соответствует некоторому предикату (Примечание: это будет допустимый объектив, если он гарантированно соответствует хотя бы одному элементу списка)
//:: (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
в сохранить дублирование поиска индекса элемента, соответствующего предикату.
как только у нас есть эта функция, мы можем построить объектив, который соответствует заданному идентификатору.
//:: 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')))))));
Это можно разложить на более мелкие функции, конечно. :)