How to aggregate and merge objects based on multiple properties?

穿精又带淫゛_ 提交于 2020-03-06 09:29:05

问题


I need your support to make a groupby in Ramda.

I have a data and I require:

  1. Sort the data by its type of service
  2. Separate those that are the same product code
  3. Make the groupby of duration
  4. 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 durations
  • key 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!