Advanced Ramda Array Transformation

Viewed 229

TLTR: Look at the javascript snippet.

I have an array of items.

I want to map items and change their structure.

I need to keep some properties and I also need to set a new property. However, the new property value is going to be based on the item that I am currently mapping.

// change title to whatever shape you desire
const customTitleTransformation = title => title

const arrayOfItems = [
  {
    id: 'some id 1',
    isSelected: true,
    title: 'some title 1',
    text: 'some text',
    description: 'some description'
  },
  {
    id: 'some id 2',
    isSelected: false,
    title: 'some title 2',
    text: 'some text',
    description: 'some description'
  },
]

// I need array of items like:
// {
//   id,
//   isSelected,
//   cells: [
//     customTitleTransformation(title),
//     text
//   ]
// }

// REGULAR JAVASCRIPT WAY  
const normalizedItems = arrayOfItems.map(item => ({
  id: item.id,
  isSelected: item.isSelected,
  cells: [
    customTitleTransformation(item.title),
    item.text,
  ]
}))

console.log('first result ', normalizedItems)

// USING RAMDA  

const normalizedItemsRamda = R.map(
  R.compose(
    R.pick(['id', 'isSelected', 'cells']),
    R.set(R.lensProp('cells'), ['how do I set the array?'])
  )
)(arrayOfItems)

console.log('ramda result ', normalizedItemsRamda)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.js"></script>

2 Answers

I think this may be a case for applySpec.

The mapObject function defines the shape of the new object. That transformation is then applied to arrayOfItems.

Whether this feels more readable is entirely left to your appreciation of course:

// change title to whatever shape you desire
const customTitleTransformation = title => title

const arrayOfItems = [{
    id: 'some id 1',
    isSelected: true,
    title: 'some title 1',
    text: 'some text',
    description: 'some description'
  },
  {
    id: 'some id 2',
    isSelected: false,
    title: 'some title 2',
    text: 'some text',
    description: 'some description'
  },
]

const mapObject = R.applySpec({
  id: R.prop('id'),
  isSelected: R.prop('isSelected'),
  cells: R.juxt([
    R.o(customTitleTransformation, R.prop('title')),
    R.prop('text')
  ])
});

const normalizedItemsRamda = R.map(mapObject, arrayOfItems)

console.log('ramda result ', normalizedItemsRamda)
console.log('\n\nOriginal Items Unchanged:')
console.log(arrayOfItems);
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.js"></script>

I'm not an expert with Ramda or FP by any stretch of the imagination, but it seems like the easiest way would be to pass a normal function into your R.compose that adds the cells property on to each object before picking the properties you want.

Let me know if this works for you, or if this is an anti-pattern and you are strictly looking for Ramda built-ins.

// change title to whatever shape you desire
const customTitleTransformation = title => title

const arrayOfItems = [
  {
    id: 'some id 1',
    isSelected: true,
    title: 'some title 1',
    text: 'some text',
    description: 'some description'
  },
  {
    id: 'some id 2',
    isSelected: false,
    title: 'some title 2',
    text: 'some text',
    description: 'some description'
  },
]

// USING RAMDA  

const normalizedItemsRamda = R.map(
  R.compose(
    R.pick(['id', 'isSelected', 'cells']),
    // shallow copy, add cells property, return new updated object
    item => R.assoc('cells', [customTitleTransformation(item.title), item.text], item)
  )
)(arrayOfItems)

console.log('ramda result ', normalizedItemsRamda)
console.log('\n\nOriginal Items Unchanged:')
console.log(arrayOfItems);
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.js"></script>

Related