Update Object Value Ramda
Solution 1:
You can use lenses here:
const titleLens = R.curry((key, id, data) => R.lensPath([
key,
R.findIndex(R.whereEq({ id }), R.propOr([], key, data)),
'title'
]));
// ----const stuff = {
"31": [
{
"id": "11",
"title": "ramda heeeelp"
},
{
"id": "12",
"title": "ramda 123"
}
],
"33": [
{
"id": "3",
"title": "..."
}
],
"4321": [
{
"id": "1",
"title": "hello world"
}
]
}
const title3111 = titleLens('31', '11', stuff);
const result = R.set(title3111, 'DID RAMDA HELP YOU?', stuff);
console.log('result', result);
<scriptsrc="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"integrity="sha256-xB25ljGZ7K2VXnq087unEnoVhvTosWWtqXB4tAtZmHU="crossorigin="anonymous"></script>
Solution 2:
I think this would be best done with a custom lens
. Here I write a lens creator (idInObjLens
) that focuses on the object with the correct id, regardless of what group it falls in. Then I write myLens
to accept an id and a property name and return you a lens that focuses on that property of the object.
With those, we can use Ramda's view
, set
, and over
to see the value, set the value, or update the value with a function:
constidInObjLens = (id) => lens (
(obj) => {
let index = -1const child = find (value => (index = findIndex (propEq ('id', id), value)) > -1, values (obj) )
if (child) return child [index]
},
(val, obj) => {
let index = -1const [key, value] = find (([key, value]) => (index = findIndex (propEq ('id', id), value)) > -1, toPairs (obj) )
return assoc (key, update (index, val, value), obj)
},
)
constmyLens = (id, key) => compose (idInObjLens (id), lensProp (key))
const stuff = {"31": [{"id": "11", "title": "ramda heeeelp"}, {"id": "12", "title": "ramda 123"}], "33": [{"id": "3", "title": "..."}], "4321": [{"id": "1", "title": "hello world"}]};
[
view (myLens ('12', 'title'), stuff),
set (myLens ('3', 'title'), 'new heading 123', stuff),
over (myLens ('1', 'title'), toUpper, stuff),
] .forEach (x =>console .log (x))
.as-console {background-color:black !important; color:lime;}
.as-console-wrapper {max-height:100%!important; top:0;}
<scriptsrc="//cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script><script>const {lens, find, findIndex, propEq, values, toPairs, assoc, update, compose, lensProp, view, set, over, toUpper } = R</script>
Note that the set
and over
will only work if that id actually exists. You might want to check using view
first.
myLens
is simple; it's just how lens composition works. (Notice that it seems to flow backward from regular compose; the technical reasons are interesting, but beyond the scope of an SO answer.) But idInObjLens
is more complex. As with all lenses, it takes a getter and a setter. Both of them simultaneously find the object key that contains the item with the id and the index of that key in the array associated with that object key. The getter simply returns the value. The setter uses assoc
to update the outer object and update
to update the array inside it. All other nested objects are simply returned by reference.
This is not code to be proud of. It works, and of course that's the main thing. But I really don't like calculating the array index as a side-effect of the find
call. Yet calculating it a second time just seems overkill. I also don't really like the name idInObjLens
and I always feel that if I don't have a good name, I'm missing something fundamental. (I don't have the same objection to myLens
, as I assume you will have a better name for this for your use-case.)
The big difference between this and the solution from Hitmand is that this lens does not require you to know up-front which key in the outer object holds the item with your id. That adds a fair bit of complexity to the solution, but makes its API much more flexible.
Solution 3:
You map all array inside properties, and use R.when
to evolve all objects with matching id
s, and replace the property's (title
in your case) value:
const { curry, map, when, propEq, evolve, always } = R
const fn = curry((id, prop, content) =>map(map( // map all objects of all propertieswhen(
propEq('id', id), // if the id of an object matchesevolve({ [prop]: always(content) })) // evolve it's property to the content
)
))
const data = {"31":[{"id":"11","title":"ramda heeeelp"},{"id":"12","title":"ramda 123"}],"33":[{"id":"3","title":"..."}],"4321":[{"id":"1","title":"hello world"}]}
const result = fn('12', 'title', 'new heading 123')(data)
console.log(result);
<scriptsrc="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"integrity="sha256-xB25ljGZ7K2VXnq087unEnoVhvTosWWtqXB4tAtZmHU="crossorigin="anonymous"></script>
Post a Comment for "Update Object Value Ramda"