javascript - node - Ramda js: lente para objetos profundamente anidados con matrices de objetos anidadas
ramda github (2)
Usando Ramda.js (y lentes), quiero modificar el objeto de JavaScript a continuación para cambiar "NAME: VERSION1" a "NAME: VERSION2" para el objeto que tiene ID = "/ 1 / B / i".
Quiero usar una lente porque solo quiero cambiar un valor profundamente anidado, pero por lo demás, mantener toda la estructura sin cambios.
No quiero usar lensIndex porque nunca sé en qué orden estarán las matrices, así que en lugar de eso, quiero "encontrar" el objeto en una matriz buscando sus campos "id".
¿Puedo hacer esto con lentes, o debo hacerlo de otra manera?
{
"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": {}
}
}
}
]
}
]
}
Aquí hay una solución:
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'')))))));
Esto podría descomponerse en funciones más pequeñas, por supuesto. :)
Esto debería ser posible mediante la creación de una lente que coincida con un objeto por ID, que luego se puede componer con otras lentes para profundizar en el campo de la imagen.
Para empezar, podemos crear una lente que se centre en un elemento de una matriz que coincida con algún predicado (nota: esto solo será una lente válida si se garantiza que coincida con al menos un elemento de la lista)
//:: (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]));
});
Tenga en cuenta que estamos construyendo manualmente la lente aquí en lugar de usar R.lens
para guardar la duplicación de la búsqueda del índice del elemento que coincide con el predicado.
Una vez que tengamos esta función, podemos construir una lente que coincida con una ID determinada.
//:: String -> Lens [{ id: String }] { id: String }
const lensById = R.compose(lensMatching, R.propEq(''id''))
Y luego podemos componer todas las lentes juntas para apuntar al campo de imagen
const imageLens = R.compose(
R.lensProp(''groups''),
lensById(''/1/B''),
R.lensProp(''apps''),
lensById(''/1/B/i''),
R.lensPath([''container'', ''docker'', ''image''])
)
Que se puede utilizar para actualizar el objeto de data
manera:
set(imageLens, ''NAME:VERSION2'', data)
A continuación, podría llevar esto un paso más allá si quisiera y declarar una lente que se centre en la versión de la cadena de imagen.
const vLens = R.lens(
R.compose(R.nth(1), R.split('':'')),
(version, str) => R.replace(/:.*/, '':'' + version, str)
)
set(vLens, ''v2'', ''NAME:v1'') // ''NAME:v2''
Esto podría luego imageLens
a la composición de imageLens
para apuntar a la versión dentro del objeto completo.
const verLens = compose(imageLens, vLens);
set(verLens, ''VERSION2'', data);