在上一个问题中,我尝试按父ID对数组进行分组,然后从每个对象-Group arrays by parent ids object Ramda中将其删除。
但是现在我有一个新问题。例如,我想更新ID为12的对象中的标题。
我的数据模型:
const stuff = {
"31": [
{
"id": "11",
"title": "ramda heeeelp"
},
{
"id": "12",
"title": "ramda 123"
}
],
"33": [
{
"id": "3",
"title": "..."
}
],
"4321": [
{
"id": "1",
"title": "hello world"
}
]
}
尝试:
const alter = (id, key, value) => pipe(
values,
flatten,
update(...) // <= end of my attempts
// then group again
)
alter('12', 'title', 'new heading 123')(stuff)
答案 0 :(得分:2)
我认为最好使用自定义lens
完成此操作。在这里,我写一个镜头创建者(idInObjLens
)聚焦于具有正确ID的对象,而不管它属于哪个组。然后我写myLens
接受ID和属性名称并返回给您聚焦物体的特性的镜头。
有了这些,我们可以使用Ramda的view
,set
和over
查看值,设置值或使用函数更新值:
const idInObjLens = (id) => lens (
(obj) => {
let index = -1
const child = find (value => (index = findIndex (propEq ('id', id), value)) > -1, values (obj) )
if (child) return child [index]
},
(val, obj) => {
let index = -1
const [key, value] = find (([key, value]) => (index = findIndex (propEq ('id', id), value)) > -1, toPairs (obj) )
return assoc (key, update (index, val, value), obj)
},
)
const myLens = (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;}
<script src="//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>
请注意,set
和over
仅在该ID实际上存在时才起作用。您可能要先使用view
进行检查。
myLens
很简单;这就是镜头组成的工作原理。 (请注意,它似乎从常规撰写中倒退了; technical reasons很有趣,但超出了SO答案的范围。)但是idInObjLens
更为复杂。与所有镜头一样,它需要吸气剂和吸气剂。他们俩同时在包含与该对象关键字关联的数组中的ID和该关键字的索引的项中找到对象关键字。 getter只是返回值。设置器使用assoc
更新外部对象,并使用update
更新内部对象。其他所有嵌套对象都只是通过引用返回。
这不是值得骄傲的代码。它有效,这当然是主要的。但是我真的不喜欢将数组索引计算为find
调用的副作用。然而,第二次计算似乎有点过头了。我也不太喜欢idInObjLens
这个名字,而且我总是觉得,如果我没有好名字,那我就会缺少一些基本知识。 (我不反对myLens
,因为我认为您会为自己的用例取一个更好的名字。)
这与Hitmand的解决方案之间的最大区别在于,该镜头不需要您预先知道外部对象中的哪个键可容纳带有您ID的物品。这给解决方案增加了相当多的复杂性,但使它的API更加灵活。
答案 1 :(得分:1)
您可以在此处使用lenses:
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);
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js" integrity="sha256-xB25ljGZ7K2VXnq087unEnoVhvTosWWtqXB4tAtZmHU=" crossorigin="anonymous"></script>
答案 2 :(得分:1)
您可以映射属性内部的所有数组,并使用R.when
来演化具有匹配的id
的所有对象,并替换属性的值(在您的情况下为title
):
const { curry, map, when, propEq, evolve, always } = R
const fn = curry((id, prop, content) =>
map(map( // map all objects of all properties
when(
propEq('id', id), // if the id of an object matches
evolve({ [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);
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js" integrity="sha256-xB25ljGZ7K2VXnq087unEnoVhvTosWWtqXB4tAtZmHU=" crossorigin="anonymous"></script>