对象数组到哈希表

问题描述 投票:2回答:6

想转换(使用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
javascript ramda.js
6个回答
2
投票

有很多方法可以解决这个问题。所有其他正确答案都有助于表明这一点。我将在下面列出我自己的几个。但首先,

为什么你的方法不起作用

你试着这样做:

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

很清楚你在这里要做什么。问题是R.prop('id')R.prop('name')是函数。 R.assoc不接受任何职能;它想要一个String(Number将实际服务)和一个任意值。所以assoc不会以这种方式使用它们。

清理它的一种尝试是认识到函数可以被认为是值的容器。在某些方面 - 也许是令人惊讶但非常有用的方式 - 函数是其返回值的容器。 R.lift旨在将处理值的函数转换为适用于值集合的函数。它的工作原理如下:R.multiply接受数字。如果我们提升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')不承认这一点。他们在累加器上寻找相关的属性,而这些属性根本就不存在。

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

一些解决方案

Simple Reduce

这两个版本中的每一个都使用简单的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的引擎优化的进行,这可能最终不会成为性能问题,但现在可能就是这样,特别是如果这是在性能关键代码中。

Using 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来创建一个单独的对象。

Using props and fromPairs

该解决方案使用R.propsR.fromPairsfromPairs接受名称 - 值对的列表(作为双元素数组)并将它们转换为单个对象。 props将命名属性拉入独立数组。 Mapping这个在原始列表中给了我们fromPairs的输入:

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

虽然我喜欢zipWith解决方案,并且如果它的输出是我想要的,将使用它,不得不添加mergeAll,使得一眼就能更难理解。所以这个解决方案在我看来是最好的Ramda选择。


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)添加到结果字典中。


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>

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)

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)

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);
© www.soinside.com 2019 - 2024. All rights reserved.