How to apply properties of an object to functions?

505 views Asked by At

I want to apply different functions to some object properties. Lets say I have this object:

const person = {
  name: 'John',
  age: 30,
  friends: [],
};

and i have some functions that i want to apply to those properties:

const upperCase = str => str.toUpperCase() //for the name

const add10 = int => int + 10 // for the age

const addFriend = (value,list) => [...list,value] // adding a friend

and this should be the result:

const person = {
  name: 'JOHN',
  age: 40,
  friends: ['Lucas']
}

What is the best way to achieve this using functional programming and point free, and if you could include examples using Ramda I would appreciate it, Thanks!

2

There are 2 answers

1
customcommander On BEST ANSWER

With Ramda, you're looking at the evolve function:

Creates a new object by recursively evolving a shallow copy of object, according to the transformation functions. All non-primitive properties are copied by reference.

You probably need to define "transform" functions on the fly; I bet you don't want to add 'Lucas' as a friend of every single person. Hopefully this is enough to get you started.

const transform =
  evolve(
    { name: toUpper
    , age: add(10)
    , friends: prepend('Lucas')
    });
    
    
console.log(

  transform(person)

);
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.min.js"></script>
<script>const {evolve, toUpper, add, prepend} = R;</script>
<script>
const person =
  { name: 'John'
  , age: 30
  , friends: []
  };
</script>


Point-free style is not the only way

⚠️

Please note that I've been able to implement this point-free style because I could hardcode each transformation functions. If you need to craft a transformation object on the fly, doing so point-free style is likely to be unreadable quickly.

7
Scott Sauyet On

As usual, customcommander gives an excellent answer. Ramda's evolve is designed for exactly that and, in the simple case, can allow for an entirely point-free answer.

One of Ramda's authors, I very much like its functions. But it should be noted that it's quite easy to build your own versions of many, including this one, especially if you're in the simple situation where you only care about updating keys at the root of your object. (Ramda's recurs into nested spec objects, which is at least a bit more involved.)

So, if you didn't have Ramda handy, you might easily write a custom version of this function like this:

const evolve = (spec, keys = Object .keys (spec)) => (obj) => 
  Object .entries (obj) .reduce ( 
    (a, [k, v]) => ({...a, [k]: keys .includes (k) ? spec [k] (v) : v})
    , {}
  )

const upperCase = str => str.toUpperCase() //for the name
const add10 = int => int + 10 // for the age
const addFriend = (value) => (list) => [...list,value] // adding a friend
const person = {name: 'John', eys: 'blue', age: 30, friends: [], hair: 'red'}

console .log (
  evolve ({name: upperCase, age: add10, friends: addFriend('Lucas')}) (person)
)

This is far from the most performant code. An excellent article from Rich Snapp explains why and how to fix it. But I personally would stick with this unless this becomes a bottleneck in my application.

Of course, as customcommander also points out, there are Ramda functions for each of your helpes: toUpper, add, append (closer to the above implementation than prepend.)

A more complete discussion on the topic VLAZ raised in a comment can be found in https://stackoverflow.com/a/33130194/1243641.