Satish
Satish

Reputation: 3100

Transforming collection using ramda

I want to do the following transformation using ramba

Input collection

const vals = [
        {metric: "Sales", measure:100, period_end_date: "2021-12-31", period_type: 'Annual' },
        {metric: "EBT", measure:101,  period_end_date: "2021-12-31", period_type: 'Annual' },
        {metric: "Sales", measure:100, period_end_date: "2021-09-30", period_type: 'Qtr' },
        {metric: "EBT", measure:101,  period_end_date: "2021-09-30", period_type: 'Qtr' }
       ]

Output

 {  
   "2021-09-30|Qtr": [{"Sales": 100}, {"EBT": 101],  
   "2021-12-31|Annual": [{"Sales": 100, }, {"EBT": 101,}] 
 }

I was able to come pretty close with this

const keyGen = compose(objOf('key'), join('|'),  props(['period_end_date','period_type']))// + '|' + prop("period_type",o) }

const valGen = compose(apply(objOf), R.ap([R.prop('metric'), R.prop('measure')]), of )

const f4 = map(compose(apply(merge), R.ap([keyGen, valGen]), of))

const result =compose(groupBy(prop('key')),f4 ) (vals)

This gives me the following result

{"2021-09-30|Qtr": [{"Sales": 100, "key": "2021-09-30|Qtr"}, 
                    {"EBT": 101, "key": "2021-09-30|Qtr"}], 
 "2021-12-31|Annual": [{"Sales": 100, "key": "2021-12-31|Annual"}, 
                       {"EBT": 101, "key": "2021-12-31|Annual"}]}

Now I need to remove the key from the inner collections. I wanted to know if there was a better alternative.

Upvotes: 0

Views: 346

Answers (3)

Scott Sauyet
Scott Sauyet

Reputation: 50807

Another possibility is something like this:

const vals = [
  {metric: "Sales", measure:100, period_end_date: "2021-12-31", period_type: 'Annual' },
  {metric: "EBT", measure:101,  period_end_date: "2021-12-31", period_type: 'Annual' },
  {metric: "Sales", measure:100, period_end_date: "2021-09-30", period_type: 'Qtr' },
  {metric: "EBT", measure:101,  period_end_date: "2021-09-30", period_type: 'Qtr' }
]

const fn = R.pipe(
  R.groupBy(({period_end_date, period_type}) => `${period_end_date}|${period_type}`),
  R.map(R.map(R.lift(R.objOf)(R.prop('metric'), R.prop('measure'))))
);

console.log(fn(vals))
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.min.js"></script>

The key point here is to lift objOf so that instead of working on values, it works on containers of values, in this case, functions that return them (prop('metric') and prop('measure').)

Upvotes: 0

Scott Christopher
Scott Christopher

Reputation: 6516

R.reduceBy can be used to group items in a list and then reduce each group of items that have the same key.

const vals = [
  {metric: "Sales", measure:100, period_end_date: "2021-12-31", period_type: 'Annual' },
  {metric: "EBT", measure:101,  period_end_date: "2021-12-31", period_type: 'Annual' },
  {metric: "Sales", measure:100, period_end_date: "2021-09-30", period_type: 'Qtr' },
  {metric: "EBT", measure:101,  period_end_date: "2021-09-30", period_type: 'Qtr' }
]

const fn = R.reduceBy(
  (list, {metric, measure}) => R.append({[metric]: measure}, list),
  [],
  ({period_end_date, period_type}) => `${period_end_date}|${period_type}`,
)

console.log(fn(vals))
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.min.js"></script>

Upvotes: 0

lukaleli
lukaleli

Reputation: 3627

Perhaps you should simplify your code and just use one simple function that reduces your collection to the desired output? I used Array.prototype.reduce which is easy translatable to Ramda's functional, point-free, curried style:

const convert = (acc, item) => {
  const name = `${item.period_end_date}|${item.period_type}`
  if (!acc[name]) acc[name] = []
  acc[name].push({ [item.metric]: item.measure })
  return acc
}

const transform = R.reduce(convert, {})
transform(vals)

And working snippet without Ramda:

const vals = [{
    metric: "Sales",
    measure: 100,
    period_end_date: "2021-12-31",
    period_type: 'Annual'
  },
  {
    metric: "EBT",
    measure: 101,
    period_end_date: "2021-12-31",
    period_type: 'Annual'
  },
  {
    metric: "Sales",
    measure: 100,
    period_end_date: "2021-09-30",
    period_type: 'Qtr'
  },
  {
    metric: "EBT",
    measure: 101,
    period_end_date: "2021-09-30",
    period_type: 'Qtr'
  }
]

const result = vals.reduce((acc, item) => {
  const name = `${item.period_end_date}|${item.period_type}`
  if (!acc[name]) acc[name] = []
  acc[name].push({
    [item.metric]: item.measure
  })
  return acc
}, {})

console.log(result)

Upvotes: 1

Related Questions