对象数组到哈希表

时间:2018-10-30 00:49:31

标签: javascript ramda.js

想转换(使用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

6 个答案:

答案 0 :(得分:2)

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

为什么您的方法不起作用

您尝试这样做:

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

很明显,您要在这里做什么。问题在于R.prop('id')R.prop('name')是函数。 R.assoc不接受功能;它需要一个字符串(实际上将使用数字)和一个任意值。因此assoc不能以这种方式与它们一起使用。

清除此错误的一种尝试是认识到可以将函数视为值的“容器” 。以某种(也许令人惊讶,但非常有用)的方式,函数是其返回值的容器。 R.lift旨在将对值起作用的函数转换为对值的容器起作用的函数。它的工作方式如下:R.multiply接受数字。如果我们 lift multiply,则结果函数将接受数字容器。像这样:

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

如果我们为lift(multiply)提供两个函数,那么我们将返回一个函数,该函数返回将它们的返回值相乘的结果:

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接受用于将两个元素合并为一个的函数。在这里,我们使用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.propsR.fromPairsfromPairs接受名称/值对的列表(作为两个元素的数组),并将它们转换为单个对象。 props将命名的属性拉入一个独立的数组。 Mapping超过原始列表,这给了我们fromPairs的输入:

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

尽管我很喜欢zipWith解决方案,并且如果它的输出是我想要的,它将使用它,而必须添加mergeAll使得一眼就很难理解。因此,这种解决方案赢得了我的最佳Ramda选择。

答案 1 :(得分:1)

您可以通过以下方法实现此目标:

 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.id的键(通过item.name命名)添加到结果字典中。

答案 2 :(得分:1)

您可以创建管道(R.pipe)来将对象数组转换为哈希:

  1. 获取每个(R.map)个对象的属性(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>

答案 3 :(得分:0)

尝试使用此代码:

   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)

答案 4 :(得分:0)

使用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)

答案 5 :(得分:0)

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

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);