更新对象值Ramda

时间:2019-11-04 12:31:52

标签: javascript functional-programming ramda.js

在上一个问题中,我尝试按父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)

3 个答案:

答案 0 :(得分:2)

我认为最好使用自定义lens完成此操作。在这里,我写一个镜头创建者(idInObjLens)聚焦于具有正确ID的对象,而不管它属于哪个组。然后我写myLens接受ID和属性名称并返回给您聚焦物体的特性的镜头。

有了这些,我们可以使用Ramda的viewsetover查看值,设置值或使用函数更新值:

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>

请注意,setover仅在该ID实际上存在时才起作用。您可能要先使用view进行检查。

myLens很简单;这就是镜头组成的工作原理。 (请注意,它似乎从常规撰写中倒退了; technical reasons很有趣,但超出了SO答案的范围。)但是idInObjLens更为复杂。与所有镜头一样,它需要吸气剂和吸气剂。他们俩同时在包含与该对象关键字关联的数组中的ID和该关键字的索引的项中找到对象关键字。 getter只是返回值。设置器使用assoc更新外部对象,并使用update更新内部对象。其他所有嵌套对象都只是通过引用返回。

这不是值得骄傲的代码。它有效,这当然是主要的。但是我真的不喜欢将数组索引计算为find调用的副作用。然而,第二次计算似乎有点过头了。我也不太喜欢idInObjLens这个名字,而且我总是觉得,如果我没有好名字,那我就会缺少一些基本知识。 (我不反对myLens,因为我认为您会为自己的用例取一个更好的名字。)

这与Hitmand的解决方案之间的最大区别在于,该镜头不需要您预先知道外部对象中的哪个键可容纳带有您ID的物品。这给解决方案增加了相当多的复杂性,但使它的API更加灵活。

答案 1 :(得分:1)

您可以在此处使用lenses

  1. 上方:https://ramdajs.com/docs/#over
  2. 设置:https://ramdajs.com/docs/#set

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>