问题
I need your support to make a groupby in Ramda.
I have a data and I require:
- Sort the data by its type of service
- Separate those that are the same product code
- Make the groupby of duration
- Make the merge of all the data
Data:
[{
'id': '1', 'serviceType': { 'description': 'GE' },
'productCode': 'codeTwo', 'duration': { 'months': 24 },
}, {
'id': '2', 'serviceType': { 'description': 'GE' },
'productCode': 'codeOne', 'duration': { 'months': 12 }
}, {
'id': '3', 'serviceType': { 'description': 'RF' },
'productCode': 'codeOne', 'duration': { 'months': 24 },
}, {
'id': '4', 'serviceType': { 'description': 'RF' },
'productCode': 'codeOne', 'duration': { 'months': 12 },
}, {
'id': '5', 'serviceType': { 'description': 'RF' },
'productCode': 'codeOne', 'duration': { 'months': 36 },
}, {
'id': '6', 'serviceType': { 'description': 'GE' },
'productCode': 'codeOne', 'duration': { 'months': 24 }
}]
Result expected:
[{
'serviceType': 'GE',
'productCode': 'codeOne',
'duration': [12,24]
}, {
'serviceType': 'RF',
'productCode': 'codeOne',
'duration': [12,24,36]
}, {
'serviceType': 'GE',
'productCode': 'codeTwo',
'duration': [24]
}]
Testing with error:
data.map(item => {
(item.serviceType === 'GE') ? listGE.push(item) : listRF.push(item)
R.compose(
R.values,
R.map(R.head),
R.map(R.prop('duration')),
R.groupBy(m => m.id !== id ? m.id : m.id)
)(listGE)
console.log(listGE);
})
listGE
回答1:
I would use reduceBy
for this task:
collect
is used to group durationskey
is used to identify a unique pair of service type & product code
It is worth noting that collect
does a bit of repetition in that it keeps reassigning the same service type and product code all the time but I think it is negligible.
// merge similar objects
const collect = (acc, obj) => ({
serviceType: obj.serviceType.description,
productCode: obj.productCode,
duration: acc.duration.concat(obj.duration.months)
});
// key generating function used to group similar objects together
const key = obj => `${obj.serviceType.description}-${obj.productCode}`;
// transformation function
const transform = compose(values, reduceBy(collect, {duration:[]}, key));
console.log(
transform(data)
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.min.js"></script>
<script>const {compose, values, reduceBy} = R;</script>
<script>
const data = [
{ id: '1',
serviceType: { description: 'GE' },
productCode: 'codeTwo',
duration: { months: 24 }},
{ id: '2',
serviceType: { description: 'GE' },
productCode: 'codeOne',
duration: { months: 12 }},
{ id: '3',
serviceType: { description: 'RF' },
productCode: 'codeOne',
duration: { months: 24 }},
{ id: '4',
serviceType: { description: 'RF' },
productCode: 'codeOne',
duration: { months: 12 }},
{ id: '5',
serviceType: { description: 'RF' },
productCode: 'codeOne',
duration: { months: 36 }},
{ id: '6',
serviceType: { description: 'GE' },
productCode: 'codeOne',
duration: { months: 24 }}];
</script>
回答2:
Another approach looks like this:
const transform = pipe (
groupBy (({serviceType: {description}, productCode}) => `${description},${productCode}`),
values,
map (applySpec ({
serviceType: path ([0, 'serviceType', 'description']),
productCode: path ([0, 'productCode']),
duration: pipe (pluck ('duration'), pluck ('months'), sort (subtract))
})),
sortWith ([ascend (prop ('productCode')), ascend (prop ('serviceType'))])
)
const data = [{"duration": {"months": 24}, "id": "1", "productCode": "codeTwo", "serviceType": {"description": "GE"}}, {"duration": {"months": 12}, "id": "2", "productCode": "codeOne", "serviceType": {"description": "GE"}}, {"duration": {"months": 24}, "id": "3", "productCode": "codeOne", "serviceType": {"description": "RF"}}, {"duration": {"months": 12}, "id": "4", "productCode": "codeOne", "serviceType": {"description": "RF"}}, {"duration": {"months": 36}, "id": "5", "productCode": "codeOne", "serviceType": {"description": "RF"}}, {"duration": {"months": 24}, "id": "6", "productCode": "codeOne", "serviceType": {"description": "GE"}}]
console.log (
transform (data)
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>
<script>const {pipe, groupBy, values, map, applySpec, path, pluck, sort, subtract, sortWith, ascend, prop} = R</script>
This first groups together all the values that have the same service type description and product code by calling groupBy
and then values
. Then for each group it calls applySpec
with a specification that yields your requested output. And finally we sort these resulting objects by product code then service type.
I'm sure that with a little thought we could make the function passed to groupBy
point-free, but I find this version readable, and can't really imagine a point-free version which is more readable.
I'm not really sure whether I prefer this or the approach from customcommander, but together they help show the variety of answers possible.
来源:https://stackoverflow.com/questions/59092224/how-to-aggregate-and-merge-objects-based-on-multiple-properties