如何使用 Ramda 按键分组并对其他键求和?
How to group by a key and sum other keys with Ramda?
假设我有一个这样的对象数组:
[
{'prop_1': 'key_1', 'prop_2': 23, 'prop_3': 45},
{'prop_1': 'key_1', 'prop_2': 56, 'prop_3': 10},
{'prop_1': 'key_2', 'prop_2': 10, 'prop_3': 5},
{'prop_1': 'key_2', 'prop_2': 6, 'prop_3': 7}
]
我想按第一个 属性 分组并对其他属性的值求和,得到如下数组:
[
{'prop_1': 'key_1', 'prop_2': 79, 'prop_3': 55},
{'prop_1': 'key_2', 'prop_2': 16, 'prop_3': 12}
]
使用 Ramda 执行此操作的正确方法是什么?
我尝试使用以下内容:
R.pipe(
R.groupBy(R.prop('prop_1')),
R.values,
R.reduce(R.mergeWith(R.add), {})
)
但这也是 'prop_1' 的总和。
您需要映射组,然后减少每个组。检查当前合并键的值。如果值为数字,则添加值。如果不只是 return 第一个值:
const { pipe, groupBy, prop, values, map, reduce, mergeWith, ifElse, is, add, identity } = R
const fn = pipe(
groupBy(prop('prop_1')),
values,
map(reduce(
mergeWith(ifElse(is(Number), add, identity)),
{}
))
)
const data = [{"prop_1":"key_1","prop_2":23,"prop_3":45},{"prop_1":"key_1","prop_2":56,"prop_3":10},{"prop_1":"key_2","prop_2":10,"prop_3":5},{"prop_1":"key_2","prop_2":6,"prop_3":7}]
const result = fn(data)
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.0/ramda.js"></script>
如果并且仅如果您提前知道密钥,您可以选择一些东西"simpler"(当然是YMMV):
reduceBy
采用类似于 Array#reduce
的函数
- 将
prop('prop_1')
用作"group by"子句
(从对象中提取值以获得最终数组应该相对简单。)
console.log(
reduceBy
( (acc, obj) =>
({ prop_1: obj.prop_1
, prop_2: acc.prop_2 + obj.prop_2
, prop_3: acc.prop_3 + obj.prop_3
})
// acc initial value
, { prop_1: ''
, prop_2: 0
, prop_3: 0
}
// group by clause
, prop('prop_1')
, data
)
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.0/ramda.min.js"></script>
<script>const {reduceBy, prop} = R;</script>
<script>
const data = [{'prop_1': 'key_1', 'prop_2': 23, 'prop_3': 45}, {'prop_1': 'key_1', 'prop_2': 56, 'prop_3': 10}, {'prop_1': 'key_2', 'prop_2': 10, 'prop_3': 5}, {'prop_1': 'key_2', 'prop_2': 6, 'prop_3': 7}];
</script>
这个答案不像Ori Drori 的答案那么简单。我不确定这是否是一件好事。这似乎更符合要求,特别是如果 "and sum the values of the other properties" 是实际要求的简化。这会尝试保留密钥 属性,然后根据您的功能组合其他密钥
const data = [
{'prop_1': 'key_1', 'prop_2': 23, 'prop_3': 45},
{'prop_1': 'key_1', 'prop_2': 56, 'prop_3': 10},
{'prop_1': 'key_2', 'prop_2': 10, 'prop_3': 5},
{'prop_1': 'key_2', 'prop_2': 6, 'prop_3': 7}
]
const transform = (key, combine) => pipe (
groupBy (prop (key)),
map (map (omit ([key]))),
map (combine),
toPairs,
map (([key, obj]) => ({prop_1: key, ...obj}))
)
console .log (
transform ('prop_1', reduce(mergeWith (add), {})) (data)
)
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.27.0/ramda.js"></script>
<script> const {pipe, groupBy, prop, map, omit, reduce, mergeWith, add, toPairs, lift, merge, head, objOf, last} = R </script>
如果你迷恋无点代码,那么最后一行可以写成
map (lift (merge) (pipe (head, objOf(key)), last))
但是由于我们已经提出 key
和 obj
的观点,我看不出有什么理由。是的,我们可以改变它,但我认为它会变得非常丑陋。
对于更像 reduce
的版本,我们可能会说一些话,而不是传递这样一个 combine
函数,我们传递一些结合了两个值的东西以及一种方法得到初始值。这留作以后的练习。
假设我有一个这样的对象数组:
[
{'prop_1': 'key_1', 'prop_2': 23, 'prop_3': 45},
{'prop_1': 'key_1', 'prop_2': 56, 'prop_3': 10},
{'prop_1': 'key_2', 'prop_2': 10, 'prop_3': 5},
{'prop_1': 'key_2', 'prop_2': 6, 'prop_3': 7}
]
我想按第一个 属性 分组并对其他属性的值求和,得到如下数组:
[
{'prop_1': 'key_1', 'prop_2': 79, 'prop_3': 55},
{'prop_1': 'key_2', 'prop_2': 16, 'prop_3': 12}
]
使用 Ramda 执行此操作的正确方法是什么? 我尝试使用以下内容:
R.pipe(
R.groupBy(R.prop('prop_1')),
R.values,
R.reduce(R.mergeWith(R.add), {})
)
但这也是 'prop_1' 的总和。
您需要映射组,然后减少每个组。检查当前合并键的值。如果值为数字,则添加值。如果不只是 return 第一个值:
const { pipe, groupBy, prop, values, map, reduce, mergeWith, ifElse, is, add, identity } = R
const fn = pipe(
groupBy(prop('prop_1')),
values,
map(reduce(
mergeWith(ifElse(is(Number), add, identity)),
{}
))
)
const data = [{"prop_1":"key_1","prop_2":23,"prop_3":45},{"prop_1":"key_1","prop_2":56,"prop_3":10},{"prop_1":"key_2","prop_2":10,"prop_3":5},{"prop_1":"key_2","prop_2":6,"prop_3":7}]
const result = fn(data)
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.0/ramda.js"></script>
如果并且仅如果您提前知道密钥,您可以选择一些东西"simpler"(当然是YMMV):
reduceBy
采用类似于Array#reduce
的函数
- 将
prop('prop_1')
用作"group by"子句
(从对象中提取值以获得最终数组应该相对简单。)
console.log(
reduceBy
( (acc, obj) =>
({ prop_1: obj.prop_1
, prop_2: acc.prop_2 + obj.prop_2
, prop_3: acc.prop_3 + obj.prop_3
})
// acc initial value
, { prop_1: ''
, prop_2: 0
, prop_3: 0
}
// group by clause
, prop('prop_1')
, data
)
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.0/ramda.min.js"></script>
<script>const {reduceBy, prop} = R;</script>
<script>
const data = [{'prop_1': 'key_1', 'prop_2': 23, 'prop_3': 45}, {'prop_1': 'key_1', 'prop_2': 56, 'prop_3': 10}, {'prop_1': 'key_2', 'prop_2': 10, 'prop_3': 5}, {'prop_1': 'key_2', 'prop_2': 6, 'prop_3': 7}];
</script>
这个答案不像Ori Drori 的答案那么简单。我不确定这是否是一件好事。这似乎更符合要求,特别是如果 "and sum the values of the other properties" 是实际要求的简化。这会尝试保留密钥 属性,然后根据您的功能组合其他密钥
const data = [
{'prop_1': 'key_1', 'prop_2': 23, 'prop_3': 45},
{'prop_1': 'key_1', 'prop_2': 56, 'prop_3': 10},
{'prop_1': 'key_2', 'prop_2': 10, 'prop_3': 5},
{'prop_1': 'key_2', 'prop_2': 6, 'prop_3': 7}
]
const transform = (key, combine) => pipe (
groupBy (prop (key)),
map (map (omit ([key]))),
map (combine),
toPairs,
map (([key, obj]) => ({prop_1: key, ...obj}))
)
console .log (
transform ('prop_1', reduce(mergeWith (add), {})) (data)
)
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.27.0/ramda.js"></script>
<script> const {pipe, groupBy, prop, map, omit, reduce, mergeWith, add, toPairs, lift, merge, head, objOf, last} = R </script>
如果你迷恋无点代码,那么最后一行可以写成
map (lift (merge) (pipe (head, objOf(key)), last))
但是由于我们已经提出 key
和 obj
的观点,我看不出有什么理由。是的,我们可以改变它,但我认为它会变得非常丑陋。
对于更像 reduce
的版本,我们可能会说一些话,而不是传递这样一个 combine
函数,我们传递一些结合了两个值的东西以及一种方法得到初始值。这留作以后的练习。