How can I avoid nested ternary expressions in my code?

36k views Asked by At

I have code like this. How can I write it in cleaner, more elegant way using functional programming in JavaScript? I want to get rid of nested ternary expressions. Any ideas?

props => ({
            iconColor: props.isPriority ? (props.isCompleted ? variables.color.lightpurple : variables.color.purple ) : variables.color.gray3,
            iconName: props.isPriority ? 'star-full' : 'star-empty',
          }))

This is rest of that code:

EDIT:

const enhance: React$HOC<*, InitialProps> = compose(
      withProps(props => ({
        iconColor: props.isPriority ? (props.isCompleted ? variables.color.lightpurple : variables.color.purple) : variables.color.gray3,
        iconName: props.isPriority ? 'star-full' : 'star-empty',
      }))
    )
5

There are 5 answers

4
Aadit M Shah On BEST ANSWER

Yes, but my linter is not happy: 44:16 error Do not nest ternary expressions [no-nested-ternary]

If that's your only problem then the solution is simple. Create your own conditional function:

const iff = (condition, then, otherwise) => condition ? then : otherwise;

props => ({
  iconColor: props.isPriority ?
    iff(props.isCompleted, variables.color.lightpurple, variables.color.purple) :
    variables.color.gray3,
  iconName: props.isPriority ? 'star-full' : 'star-empty',
})

Now your linter shouldn't complain. That being said, you should disable [no-nested-ternary] in your linter. It's kind of stupid that your linter thinks that nested conditionals are bad.

1
Rajesh On

I agree with @JaromandaX about using a function. That keeps your JSX clean and your logic reusable.

Apart from that, to avoid nested ternary operator, you can try something like this:

  • Keep an array/map of all possible values
  • Based on flags, create a binary string and convert it into number.
  • return the value at provided index

function withTernary(a, b){
  return a ? (b ? 'test': 'foo') : 'bar';
}

function withoutTernary(a, b){
  var result = ['bar', undefined, 'foo', 'test'];
  // or may be a map
  /*
  * map = {
  *    '00': 'bar',
  *    '10': 'foo',
  *    '11': 'test'
  * }
  */
  var index = parseInt((+a) + '' + (+b), 2);
  return result[index];
}

var testCase = [[0,0], [1, 0], [1,1]];

testCase.forEach(x => {
  console.log('With Ternary:', withTernary(...x));
  console.log('Without Ternary:', withoutTernary(...x));
})

2
Jonas Wilms On

One could use an IIaFE ( immeadiately invoked arrow function expression) :

props => ({
        iconColor:(_=>{
           if(props.isPriority){
             if(props.isCompleted){
               return variables.color.lightpurple;
             }
             return variables.color.purple;
           }
           return variables.color.gray3;
        })(),
        iconName:(_=>{ 
           if(props.isPriority){
              return 'star-full';
           }
           return 'star-empty';
       })()
}))
1
TheQuickBrownFox On

In this case you could consider inverting the condition to remove the "unnatural" nesting of ternaries. There would still be nesting, but it can be written flat - without parentheses - which means that you can lay it out neatly across multiple lines:

props => ({
    iconColor:
        !props.isPriority ? variables.color.gray3
        : props.isCompleted ? variables.color.lightpurple
        : variables.color.purple,
    iconName: props.isPriority ? 'star-full' : 'star-empty',
})

The downside of this is use of a negative condition, which I usually try to avoid as they're a bit harder to follow than positive conditions.

Yet another option is to flatten the conditions with &&:

props => ({
    iconColor:
        props.isPriority && props.isCompleted ? variables.color.lightpurple
        : props.isPriority ? variables.color.purple
        : variables.color.gray3,
    iconName: props.isPriority ? 'star-full' : 'star-empty',
})
1
vipul goyal On

I will suggest, simply use below:

    props => ({
      iconColor: ${(props) => {
      if (props.isPriority) {
        return props.isCompleted
          ? variables.color.lightpurple
          : variables.color.purple;
      }
      return variables.color.gray3;
    }};
      iconName: props.isPriority ? 'star-full' : 'star-empty',
    })