如何将对象列表转换为带键的数组/对象?

4
我尝试使用Ramda编写代码,仅使用原始对象的idcomment键来生成新的数据结构。我对Ramda很陌生,但我有使用Python进行类似编码的经验。 给定以下初始数据结构...
const commentData = {
  '30': {'id': 6, 'comment': 'fubar', 'other': 7},
  '34': {'id': 8, 'comment': 'snafu', 'other': 6},
  '37': {'id': 9, 'comment': 'tarfu', 'other': 42}
};

我想把它转化为这样...

{
  '6': 'fubar',
  '8': 'snafu',
  '9': 'tarfu'
}

我在Ramda的菜谱中找到了以下 例子,它与要求相似...

const objFromListWith = R.curry((fn, list) => R.chain(R.zipObj, R.map(fn))(list));
objFromListWith(R.prop('id'), R.values(commentData));

但它所返回的值包括整个原始对象作为值......

{
  6: {id: 6, comment: "fubar", other: 7},
  8: {id: 8, comment: "snafu", other: 6},
  9: {id: 9, comment: "tarfu", other: 42}
}

如何把值仅保留为它们的comment键对应的值?

我不需要使用参考书中提供的代码。如果有更好(更简单、更短、更高效)的代码可以得到所需结果,我会很乐意使用。


啊 @ScottSauyet 我从控制台复制了错误的版本。;) 是的,需要加上括号()。 - epascarello
请查看@ScottSauyet在下面的答案中的更正。 - Mr. Lance E Sloan
2个回答

4

如果您不介意,您不需要使用Ramda,纯JS可以很好地处理它:

您可以使用Object.values()的组合,获取第一个对象(commentData)的所有值,并使用.forEach()(或者甚至使用.map(),但速度较慢)在由Object.values结果形成的数组中将值动态插入到新对象中。

const commentData = {
  '30': {'id': 6, 'comment': 'fubar', 'other': 7},
  '34': {'id': 8, 'comment': 'snafu', 'other': 6},
  '37': {'id': 9, 'comment': 'tarfu', 'other': 42}
};

let values = Object.values(commentData)
let finalObj = {};

values.forEach(x => finalObj[x.id] = x.comment)

console.log(finalObj)

但是,如果你想要一个单行代码,可以使用Object.fromEntries(),在返回基于idcomment的键/值数组后,使用.map(),如下所示:

const commentData = {
  '30': {'id': 6, 'comment': 'fubar', 'other': 7},
  '34': {'id': 8, 'comment': 'snafu', 'other': 6},
  '37': {'id': 9, 'comment': 'tarfu', 'other': 42}
};

console.log(Object.fromEntries(Object.values(commentData).map(x => [x.id, x.comment])))


我之前尝试过一种非 Ramda 的方法,但是我并没有完全实现。我正在使用别人编写的 Vue.js 应用程序。我认为使用 Ramda 可能很重要,因为它返回一个 Promise 而不是实际值,这正是 Vue 所需要的。现在我已经调试了应用程序,并发现我遇到了与 Vue 相关的问题。我相信如果解决了这个问题,我就能够以这种方式使用转换后的数据。如果您知道使用 Ramda 的解决方案,我仍然对看看它是如何实现的感兴趣。 - Mr. Lance E Sloan
我对Ramda和Vue都没有了解,抱歉。 - Calvin Nunes
在这里使用 map 会产生什么样的副作用? - Mr. Lance E Sloan
@LS:那个评论是在被替换为上面所示的forEach之前留下的。我总是更喜欢使用map/find/filter或者必要时使用reduce。但它们有着有意义的语义,仅仅为了修改一个外部变量而使用它们感觉很不好。 (我已经删除了引发所有这些问题的评论,因为问题已经解决。) - Scott Sauyet
@CalvinNunes:很高兴能帮到你。我个人认为,在测试并发现我的代码太慢,然后找到并修复最糟糕的热点之前,不要花太多时间考虑性能。 - Scott Sauyet
显示剩余6条评论

4
一个 Ramda 一行代码的例子是:

const foo = compose(fromPairs, map(props(['id', 'comment'])), values)

const commentData = {
  '30': {'id': 6, 'comment': 'fubar', 'other': 7},
  '34': {'id': 8, 'comment': 'snafu', 'other': 6},
  '37': {'id': 9, 'comment': 'tarfu', 'other': 42}
}

console.log(
  foo(commentData)
)
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>
<script>const {compose, fromPairs, map, props, values} = R           </script>

但这似乎并不比epascarello评论中的建议更加简洁:

const foo = (obj) => 
  Object.values(obj).reduce((o, v) => (o[v.id] = v.comment) && o, {})

如果不会影响性能,我会写类似的代码:

const foo = (obj) => 
  Object.values(obj).reduce((o, {id, comment}) => ({...o, [id]: comment}), {})

你在另一个解决方案的评论中提到,在那种情况下使用map会产生副作用。这种情况有什么不同之处,让你可以放心地使用map?此外,你提供的最后一种解决方案可能会出现什么性能问题? - Mr. Lance E Sloan
@LS:我已经在那里回复了。当我写下那个注释时,那段代码看起来像是values.map(x => finalObj[x.id] = x.comment),这让我觉得它滥用了map。可能的性能问题由Rich Snapp描述。只要性能测试没有显示出问题,我选择使用这种模式来表达它。但是有一个避免它的论点。 - Scott Sauyet

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接