要散列的对象数组 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) 将您的对象数组转换为散列:
- 获取每个 (R.map) objects' properties (R.props) 的值。
- 将对数组转换为对象 (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
以创建单个对象。
使用 props
和 fromPairs
此解决方案在原始列表上使用 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 的最佳选择。
想转换(使用 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) 将您的对象数组转换为散列:
- 获取每个 (R.map) objects' properties (R.props) 的值。
- 将对数组转换为对象 (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
以创建单个对象。
使用 props
和 fromPairs
此解决方案在原始列表上使用 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 的最佳选择。