要散列的对象数组 Table

Object Array to Hash Table

想转换(使用 Ramda)

var a = [{id: 1, name: 'one'}, {id: 2, name: 'two'},{id: 3, name: 'three'}]

进入

var b = {1:'one', 2:'two', 3:'three'}

我是函数式编程的新手,我愿意接受任何想法。

我认为我们必须做的是,使用以 {} 开头的 reduce 函数,然后我们希望在每次迭代时将当前元素的 ID 和名称添加到哈希表中。这就是我想出的,看起来非常错误。我接近了吗?

var a = [{id: 1, name: 'one'}, {id: 2, name: 'two'},{id: 3, name: 'three'},{id: 4, name: 'four'} ]
var b = R.reduce(R.assoc(R.prop('id'), R.prop('name')), {}, a)
b

您可以通过以下方式实现:

 var a = [{id: 1, name: 'one'}, {id: 2, name: 'two'},{id: 3, name: 'three'}]

 var b = R.reduce((dict, item) => ({ ...dict, [ item.id ] : item.name }), {}, a)

此方法使用 es6 语法在每次缩减迭代期间将具有值 (item.name) 的键(通过 item.id 命名)添加到生成的字典中。

尝试使用此代码:

   var a = [{id: 1, name: 'one'}, {id: 2, name: 'two'},{id: 3, name: 'three'}]
    R.map(i=> {
      var n = []
      n[R.keys(i)[0]] = i.name
      return n
    },a)

当使用 es6 时,您可以获得更灵活的解决方案

let a = [{id: 1, name: 'one'}, {id: 2, name: 'two'},{id: 3, name: 'three'}];
let out = a.reduce((acc, {id, name}) => {
  acc[id] = name;
  return acc;
}, {});
console.log(out)

您可以循环初始数组并分配给新对象:

var a = [{id: 1, name: 'one'}, {id: 2, name: 'two'},{id: 3, name: 'three'}];
var new_a = {};

a.forEach(function(e) {
    new_a[e.id] = e.name;
});

console.log(new_a);

您可以创建管道 (R.pipe) 将您的对象数组转换为散列:

  1. 获取每个 (R.map) objects' properties (R.props) 的值。
  2. 将对数组转换为对象 (R.fromPairs)。

const a = [{id: 1, name: 'one'}, {id: 2, name: 'two'},{id: 3, name: 'three'}];

const convertToHash = (props) => 
  R.pipe(
    R.map(R.props(props)),
    R.fromPairs
  );
  
const result = convertToHash(['id', 'name'])(a);  
  
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.min.js"></script>

有许多可行的方法。所有其他正确答案都有助于证明这一点。我将在下面列出一些我自己的。但首先,

为什么你的方法不起作用

您尝试这样做:

R.reduce(R.assoc(R.prop('id'), R.prop('name')), {}, a)

很明显你想在这里做什么。问题是 R.prop('id')R.prop('name') 是函数。 R.assoc 不接受函数;它需要一个字符串(实际上会提供数字)和一个任意值。所以 assoc 不会以这种方式处理它们。

清理它的一个尝试是认识到函数可以被认为是值的容器。在某些——也许令人惊讶但非常有用的——方式中,函数是其 return 值的容器。 R.lift 旨在将对值起作用的函数转换为对值的 容器 起作用的函数。它是这样工作的:R.multiply 接受数字。如果我们 lift multiply,结果函数接受数字容器。像这样:

R.lift(R.multiply)(Maybe.Just(5), Maybe.Just(3))  // Maybe.Just(15)

如果我们为 lift(multiply) 提供两个函数,那么我们会得到一个 return 将它们的 return 值相乘的结果的函数:

const area = R.lift(R.multiply)(prop('width'), prop('height'))
area({width: 5, height: 3})

所以也许我们可以用 lift:

更新你的技术
R.reduce(R.lift(R.assoc)(R.prop('id'), R.prop('name'), identity), {}, a)

不幸的是,这又失败了。这次的麻烦是 reduce 接受二进制回调,同时提供累加器和当前值。但是 R.prop('id')R.prop('name') 不承认这一点。他们在累加器上寻找相关属性,但根本不存在。

我们可能仍然能够解决这个问题,但在这一点上,我们将失去这个解决方案的大量简单性。那么让我们看看其他的可能性。

一些解决方案

简单减少

这两个版本中的每一个都使用一个简单的 reduce 调用,就像您的解决方案试图做的那样:

const convertToHash = reduce((acc, {id, name}) => merge(acc, {[id]: name}), {})

const convertToHash = reduce((acc, {id, name}) => ({...acc, [id]: name}), {})

它们非常相似。两者都在每次迭代中创建一次性对象。在第一个中,您可以将 R.merge 替换为 Object.assign 而不会出现任何实际问题,因为由此产生的突变是函数内部的。第二个看起来稍微优雅一些​​,但它会在每次迭代时重建整个累加器。随着 ES6 引擎优化的进行,这可能最终不会成为性能问题,但现在可能是,尤其是在性能关键代码中。

使用zipWith

Ramda 的 zip 函数接受两个列表并将它们逐个位置组合成一个列表。 R.zipWith accepts a function used to combine the two elements into one. Here we use R.objOf,它将名称和值转换为单个 属性 对象。 (objOf('foo', 42) //=> {foo: 42}.):

const convertToHash = compmose(mergeAll, lift(zipWith(objOf))(pluck('id'), pluck('name')))

如上所述,我们使用 lift 使这个 zipWith(objOf) 与函数一起工作。这导致类似 [{"1": "one"}, {"2": "two"}, {"3": "three"}] 的结果,然后我们将其传递给 R.mergeAll 以创建单个对象。

使用 propsfromPairs

此解决方案在原始列表上使用 R.props and R.fromPairs. fromPairs accepts a list of name-value pairs (as two-element arrays) and turns them into a single object. props pulls the named properties into a stand-alone array. Mapping 为我们提供 fromPairs 的输入:

const convertToHash = compose(fromPairs, map(props(['id', 'name'])))

虽然我喜欢 zipWith 解决方案,如果它的输出是我想要的,我会使用它,但必须添加 mergeAll,让人很难一目了然。因此,这个解决方案在我看来是 Ramda 的最佳选择。