想转换(使用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
有很多方法可以解决这个问题。所有其他正确答案都有助于表明这一点。我将在下面列出我自己的几个。但首先,
你试着这样做:
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')
不承认这一点。他们在累加器上寻找相关的属性,而这些属性根本就不存在。
我们仍然可以解决这个问题,但在这一点上,我们将失去这个解决方案的大部分简单性。那么让我们看看其他可能性。
这两个版本中的每一个都使用简单的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
来创建一个单独的对象。
props
and fromPairs
该解决方案使用R.props
和R.fromPairs
。 fromPairs
接受名称 - 值对的列表(作为双元素数组)并将它们转换为单个对象。 props
将命名属性拉入独立数组。 Mapping
这个在原始列表中给了我们fromPairs
的输入:
const convertToHash = compose(fromPairs, map(props(['id', 'name'])))
虽然我喜欢zipWith
解决方案,并且如果它的输出是我想要的,将使用它,不得不添加mergeAll
,使得一眼就能更难理解。所以这个解决方案在我看来是最好的Ramda选择。
您可以通过以下方式实现此目的:
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
)添加到结果字典中。
您可以创建一个管道(R.pipe)将您的对象数组转换为哈希:
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>
尝试使用此代码:
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);