-1

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
customcommander
  • 17,580
  • 5
  • 58
  • 84

2 Answers2

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>
customcommander
  • 17,580
  • 5
  • 58
  • 84
1

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.

Scott Sauyet
  • 49,207
  • 4
  • 49
  • 103