Toby Hede
Toby Hede

Reputation: 37133

Update nested values based on a condition using Ramda

I am trying to update a value in an array in a nested object structure using Ramda.

I want to update the value prop of the object in C with the name blah

const o = {
  A: {
    B: {
      C: [
        {name: 'blah', value: 'blah'},
        {name: 'vtha', value: 'blah'},
      ]
    }
  }
}

I've been plying with lenses, but it seems quite verbose and I figure I am doing it wrong.

Ideally, I would love something that returns a new object.

const res = fn(o)


{
  A: {
    B: {
      C: [
        {name: 'blah', value: 'vtha'}
        {name: 'vtha', value: 'blah'},
      ]
    }
  }
}

Upvotes: 2

Views: 4185

Answers (3)

Chad S.
Chad S.

Reputation: 6633

To update the array in the object we need the path to the array. Then we can update the result with a lens and a helper function adjustIfSatisfies

const { propEq, identity, over, assoc, curry, lensPath, map, ifElse } = R
const o = {
  A: {
    B: {
      C: [
        {name: 'blah', value: 'blah'},
        {name: 'vtha', value: 'blah'},
      ]
    }
  }
}

const adjustIfSatisfies = curry(
  (pred, updateFn, samples) => {
    return map(ifElse(pred, updateFn, identity), samples)
  }
)

const result = over(
  lensPath(['A', 'B', 'C']), 
  adjustIfSatisfies(
    propEq('name', 'blah'), 
    assoc('value', 'vtha')
  ),
  o
)

console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.min.js"></script>

Upvotes: 0

Yury Tarabanko
Yury Tarabanko

Reputation: 45121

If you want to do it in immutable way it would be like

const fn = over(
  R.lensPath(['A', 'B', 'C']),
  R.map(
    o => o.name === 'blah' ? R.assoc('value', 'vtha', o): o
  )
)

Demo.

If you want to avoid manually defining functions by all means you could replace mapped function with

R.ifElse(
  R.propEq('name', 'blah'),
  R.assoc('value', 'blah'),
  R.identity
)

But imho ternary operator looks more readable here.

Upvotes: 4

Scott
Scott

Reputation: 54

You could try using filter and where, i wrote an example below.

const o = {
  A: {
    B: {
      C: [
        {name: 'blah', value: 'blah'},
        {name: 'vtha', value: 'blah'},
      ]
    }
  }
};

var pred = R.filter(R.where({name: R.contains('blah')}));

pred(o.A.B.C);

Upvotes: 1

Related Questions