如何使用 ramda 表达式编辑数组中的对象?

How to edit an object inside array using ramda expressions?

我有一个这样的数组:

[
  {
    id: '1852',
    label: 'One',
    types: [1, 2, 4]
  },
  {
    id: '1854852',
    label: 'Two',
    types: [1, 2]
  },
  {
    id: '4581852',
    label: 'Three',
    types: [1]
  }
]

id属性是唯一的,我用它来获取对象

我有一个对象的变化(例如第三个),我希望它是这样的:

  {
    id: '4581852',
    label: 'new Three',
    types: [1, 7, 9]
  }

如何对数组进行适当的更改?

因为现在我在做一个不好的解决方案,我试图通过它的id找到元素,从数组中删除它,然后将新对象推入数组,但它总是在最后当然,这不适合我。

如有任何帮助,我们将不胜感激。

您可以使用Array#find获取对象,然后直接更新其属性。

const arr = [
  {
    id: '1852',
    label: 'One',
    types: [1, 2, 4]
  },
  {
    id: '1854852',
    label: 'Two',
    types: [1, 2]
  },
  {
    id: '4581852',
    label: 'Three',
    types: [1]
  }
]
const obj = arr.find(({id})=>id==='4581852');
obj.label = 'new Three';
obj.types.push(7,9);
console.log(arr);

您可以在 list 中查找 id 并将 object 替换为您找到的内容。 请记住,这种方式 object 必须包含所有属性。

const replace = (list, id, object) => {
  const index = list.findIndex(item => item.id === id);
  return [...list.slice(0, index), object, ...list.slice(index + 1)]
}

替换 2nd 元素的示例用法:

const list = [
  {
    id: '1852',
    label: 'One',
    types: [1, 2, 4]
  },
  {
    id: '1854852',
    label: 'Two',
    types: [1, 2]
  },
  {
    id: '4581852',
    label: 'Three',
    types: [1]
  }
]

const nextList = replace(list, "1854852", { id: "1854852", label: 'Other Three', types: [1, 2, 3] })

首先,Ramda 不会 帮助您编辑您的值,如果“编辑”是指就地更改。 Ramda(免责声明:我是作者之一)非常注重处理不可变数据。然而,它有许多技术可以帮助您创建数据的更改副本。不过,我不确定它们是否值得,因为相当简单的 ES6 版本也一样干净。

如果您确实想使用 Ramda,一个版本可能如下所示:

const updateAt = (id, transform) => 
  map (when (propEq ('id', id), transform))

const data = [{id: '1852', label: 'One', types: [1, 2, 4]}, {id: '1854852', label: 'Two', types: [1, 2]}, {id: '4581852', label: 'Three', types: [1]}]

console.log ( 'Setting the value',
  updateAt ('4581852', assoc ('label', 'new Three')) (data)
)

console .log ( 'Updating the value',
  updateAt ('1852', evolve ({'label': concat('new ')})) (data)
)

console .log ( 'Original data unchanged', data)
.as-console-wrapper {min-height: 100% !important; top: 0}
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.27.0/ramda.js"></script>
<script> const {map, when, propEq, assoc, evolve, concat} = R </script>

我们展示了两种不同的调用方式:一种是我们将 label 属性 与您的新值相关联。在第二个中,我们通过将 'new ' 和当前值连接在一起来改进您的对象。

如果我们愿意,我们可以写一个无积分版本。 Ramda 从早期开始就包含了使之成为可能的工具,尽管它们至少在某种程度上不受核心团队的青睐。其中之一是useWith,我们可以用它来写这个函数

const updateAt = compose (map, useWith (when, [propEq ('id')]))

不过,我不推荐这样做。初始版本似乎很容易阅读和理解。这个版本更紧凑,但似乎并没有真正从最初的版本中获得任何其他东西,而且可以说更难理解。


第二种可能是使用镜头。 (有几个很好的介绍,包括来自 Richard Tan.) Lenses allow you to focus on a particular part of a data structure, in order to view the value, set it directly, or alter it with a function. Ramda provides these general-purpose functions in view, set, and over. It also has a few function to build lenses, such as lensProp, lensIndex, and lensPath. But we are free to use lens 的一个,用于构建我们自己的,传递 getter 和 setter 函数。

因此我们可以编写一个镜头来查找列表中与特定 属性 匹配的值,并使用它来构建我们的函数。它可能看起来像这样:

const lensMatch = (propName) => (key) => 
  lens ( 
    find (propEq (propName, key)),
    (val, arr, idx = findIndex (propEq (propName, key), arr)) =>
        update(idx > -1 ? idx : length (arr), val, arr)
  )

const idLens = lensMatch('id')

const setLabelById = (id, val) =>
  set(compose(idLens(id), lensProp('label')), val)

const updateLabelById = (id, fn) =>
  over(compose(idLens(id), lensProp('label')), fn)


const data = [{id: '1852', label: 'One', types: [1, 2, 4]}, {id: '1854852', label: 'Two', types: [1, 2]}, {id: '4581852', label: 'Three', types: [1]}]


console .log ( 'Setting the value',
  setLabelById ('4581852', 'new Three') (data)
)

console .log ( 'Updating the value',
  updateLabelById('4581852', concat('new '))(data)
)
.as-console-wrapper {min-height: 100% !important; top: 0}
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.27.0/ramda.js"></script>
<script> const {lens, find, propEq, findIndex, update, length, over, compose, lensProp, set, concat} = R </script>

同样,我们编写了两个不同版本的函数,具体取决于您是只想设置值还是用函数更新它。

这比原来的版本多了很多代码,所以有人可能会问有什么好处。答案很简单:我们在这里得到两个非常有用的可重用函数:lensMatchidLenslensMatch 让我们定义一个 属性 来检查并关注与 属性 上的给定值匹配的(第一个)元素。 idLens 专门用于 属性 id。它们有更多的潜在用途并且非常有价值,Ramda 团队真的应该考虑包括它们。 (自我注释...)


稍微想一想,我们可能会想出更多的 Ramda 函数组合来帮助解决这个问题。但我怀疑这是否真的值得,因为你的具体问题可以用一个足够简单的 ES6 函数来解决,根本不用 Ramda。

const setLabelById = (id, val, data, index = data.findIndex(({id: i}) => id == i)) =>
  index < 0
    ? data
    : [...data.slice(0, index), {...data[index], label: val}, ...data.slice(index + 1)]


const data = [{id: '1852', label: 'One', types: [1, 2, 4]}, {id: '1854852', label: 'Two', types: [1, 2]}, {id: '4581852', label: 'Three', types: [1]}]

console .log (
  setLabelById ('4581852', 'new Three', data)
)
.as-console-wrapper {min-height: 100% !important; top: 0}

我们可以用同样的方式轻松编写一个 updateLabelById 函数。

这项技术与 Dave 的回答有相当多的重叠,但它是从旧条目构建新条目,仅替换 label 属性.


我会非常警惕“使用 Ramda 表达式”这个短语。 Ramda 是一种旨在使以某种风格编写代码更容易的工具。它不是一个框架。除非你将编写代码作为练习来学习更多关于 Ramda 的知识,否则是否使用 Ramda 应该是一个问题,它是否使你的代码更易于阅读和编写。它不应该是它自己的目标。