Can ternary operators contain multiple actions?

It is very-very important to note that : Can we use multiple actions in truthy/falsy part?

eg. if (condition)
{
action-1;
action-2;

}
else
{
action-n; //n th action
action-(n+1); // n+1 th action

}

in ternary form???

7 Likes

Yes, we can, but with one proviso… There is no block demarcation so that action should be simple, else it would be better to abstract it away in a function and call the function from the ternary.

Something we commonly see is console.log() as an action in each branch…

a % 2 === 0 ? console.log(`${a} is even.`) : console.log(`${a} is odd.`);

These are both simple actions that call the same function. Now let’s consider a couple things:

  • a ternary is an expression
  • console.log() accepts any expression and parses it as text characters to the console as best it can.

That means we don’t have to call the method in each branch, we can pass the entire ternary as the argument.

console.log(a % 2 === 0 ? `${a} is even.` : `${a} is odd.`);

All expressions boil down to one value. It’s that value that will be logged in this instance. The point here is that ternary expressions should be simple if they are to be easy to read and comprehend. They are not a de facto replacement for if..else, only a convenience to lighten the code.

Above I mentioned that ternaries have no block demarcation. Well, that’s not entirely true since JS recognizes ALL blocks, even if they have no keyword or identifier associated.

a % 2 === 0 ? {
    console.log(`${a} is even.`)
} : {
    console.log(`${a} is odd.`)
};

Now the reader can see the blocks and if they contain a few lines of code it will still be clear which branch of the ternary they are in. I rather doubt many teachers would go along with this, though.

Bottom line, keep your ternary expressions simple and well defined. The moment they get complicated, the reader could get muddled. That could make for some heady maintenance work that might otherwise go smoothly.

const doubleX = x => x * 2;
const halfX = x => x / 2;

const messingAround = a => a % 2 === 0 ? halfX(a) : doubleX(a);

See how the functions help abstract away the maths?

45 Likes

Addendum

We can return a function reference without invoking it, effectively giving the caller an object that can be called multiple times in the ensuing session.

const callbackChooser = a => a % 2 === 0 ? halfX : doubleX;

https://repl.it/@mtf/callbackChooser

6 Likes

For those who are curious about else if statement in ternary operator

function example(…) {
    return condition1 ? value1
         : condition2 ? value2
         : condition3 ? value3
         : value4;
}

// Equivalent to:

function example(…) {
    if (condition1) { return value1; }
    else if (condition2) { return value2; }
    else if (condition3) { return value3; }
    else { return value4; }
}

Source MDN

31 Likes

You can chain/nest and have all kinds of fun with ternaries but whether you should is the bigger question and the answer is “it depends”.

Here’s the same code factored four different ways (comment out the other three and run it); the question is which is more desirable? Readable? Maintainable? (these are subjective):

function convertScoreToGradeWithPlusAndMinus(score) {
  if (score > 100 || score < 0) return `INVALID SCORE`;
  if (score < 60) return `F`;
  if (score > 97) return `A+`;

  let letter = `D`;
  if (score > 89) letter = `A`;
  if (score > 79) letter = `B`;
  if (score > 69) letter = `C`;

  let suffix = ``;
  let ones = score % 10;
  if (ones > 7) suffix = `+`;
  if (ones < 3) suffix = `-`;

  return letter + suffix;
}

function convertScoreToGradeWithPlusAndMinus(score) {
  if (score > 100 || score < 0) return `INVALID SCORE`;
  if (score < 60) return `F`;
  if (score > 97) return `A+`;
  let r = score % 10;
  return (score > 89 ? `A`
        : score > 79 ? `B`
        : score > 69 ? `C`
        : `D`).concat(
            r > 7 ? `+`
          : r < 3 ? `-`
          : ``);
}

const convertScoreToGradeWithPlusAndMinus = score => 
  (score > 100 || score < 0) ? `INVALID SCORE`
    : (score < 60) ? `F`
    : (score > 97) ? `A+`
    : (score > 89 ? `A`
      : score > 79 ? `B`
      : score > 69 ? `C`
      : `D`).concat(
          score % 10 > 7 ? `+`
        : score % 10 < 3 ? `-`
        : ``);

const convertScoreToGradeWithPlusAndMinus = score =>
  (score > 100 || score < 0) ? `INVALID SCORE` : (score < 60) ? `F` : (score > 97) ? `A+` : 
    (score > 89 ? `A` : score > 79 ? `B` : score > 69 ? `C` : `D`)
      .concat(score % 10 > 7 ? `+` : score % 10 < 3 ? `-` : ``);


for (let score = -1; score < 102; score++)
  console.log(score, convertScoreToGradeWithPlusAndMinus(score));

10 Likes

Hello Its possible to use ternary operator for else if or its only for if or else statement?

what are the example for else if ?

Thankz

Another one of those questions I am remiss to have not intercepted in time. Please accept my apology for responding so late.

We can nest ternary expressions, which is how we emulate else if.

f = a ? u : b ? v : c ? w : d

where a, b, and c are states that could well be the same variable; and u, v, w are return values or function references; and, d is the default return value or function reference.

What’s not to love? I love this form of expression. Will I be able to understand it six months from now? Can I justify its use right now? Just because something looks cool doesn’t make it cool. All these convenience features come with their own pitfalls and trapdoors. Bottom line, know what you’re dealing with and make informed choices.

8 Likes

Can you please explain how this works?

This actually returns an error as every value in an object must be associated with a key

a % 2 === 0 ? {
    a: console.log(`${a} is even.`)
} : {
    a: console.log(`${a} is odd.`)
};

However, I still don’t understand how this works!

const callbackChooser = a => a % 2 === 0 ? halfX : doubleX;

Depending upon the parity of a, halfX is passed when a is even, and doubleX is passed when a is odd. Given a callback reference, they can be invoked at a later time by some higher order function such as an iterator or an event handler.

Or it could be a sort of function factory…

const doubleX = x => x * 2;
const halfX = x => x / 2;

const callbackChooser = a => a % 2 === 0 ? halfX : doubleX;

let f;
f = callbackChooser(9)
console.log(f(10))    //  20
console.log(f(15))    //  30

f = callbackChooser(42)
console.log(f(42))    //  21
console.log(f(84))    //  42

I don’t really understand this code. I understand that the doubleX function takes an argument and doubles it, and the halfX function takes an argument and halves it. However, I don’t get what callbackChooser actually does. I also don’t understand the rest of the code.

We have a choice of two functions, depending upon the parity of a. Our callbackChooser does not invoke the functions, only returns a reference to one or the other of them. We can assign that reference to a generic variable and then invoke it.

Above we assigned the reference to f. When we pass an odd number to our function factory, it returns the doubleX reference. When we invoke that function on any number it will return half that number. When we pass an even number to the factory, it returns the halfX reference, which when invoked on any number will return its double.

What’s more, we don’t have to assign the reference. We can invoke it upon its return…

 console.log(callbackChooser(9)(10))    //  20

Given a function, say add(), when we assign only the reference we are simply giving the function another name, without changing anything.

const add = (a, b) => a + b;

f = add

console.log(f(6, 7))    //  13

This will come up at some point, namely pass by reference and higher order functions.

1 Like

Thank you so much! This really helped!

Just to clarify, when you said:

did you mean:

When we pass an odd number to our function factory, it returns the doubleX reference. When we invoke that function on any number it will return double that number.

and when you said:

did you mean:

When we pass an even number to the factory, it returns the halfX reference, which when invoked on any number will return its half.

D’oh! Yeah, what you said. Got me head in the clouds today, it would seem.

1 Like

I know its been a year you posted that but I thought it might be good to mention that your code example is a great feedback of how far you can take it with ternaries - or not :wink:
But I also want to mention that by the reading of the code, I have the feeling the score would always be ‘C’ for the first function as you stack the if statements. So, I’d rather use “else if” within the first function.

Beside that, I love that example! :smiley:

Nice catch and thank you!

let that be a lesson in proper unit testing :smiley:

…and another ternary; fixed code w/test:

function convertScoreToGradeWithPlusAndMinus1(score) {
  if (score > 100 || score < 0) return `INVALID SCORE`;
  if (score < 60) return `F`;
  if (score > 97) return `A+`;

  let letter = `D`;
  if (score > 89) letter = `A`;
  else if (score > 79) letter = `B`;
  else if (score > 69) letter = `C`;

  let suffix = ``;
  let ones = score % 10;
  if (ones > 7) suffix = `+`;
  if (ones < 3) suffix = `-`;

  return letter + suffix;
}

function convertScoreToGradeWithPlusAndMinus2(score) {
  if (score > 100 || score < 0) return `INVALID SCORE`;
  if (score < 60) return `F`;
  if (score > 97) return `A+`;
  let r = score % 10;
  return (score > 89 ? `A`
    : score > 79 ? `B`
      : score > 69 ? `C`
        : `D`).concat(
    r > 7 ? `+`
      : r < 3 ? `-`
      : ``);
}

const convertScoreToGradeWithPlusAndMinus3a = score =>
  (score > 100 || score < 0) ? `INVALID SCORE`
    : (score < 60) ? `F`
    : (score > 97) ? `A+`
      : (score > 89 ? `A`
        : score > 79 ? `B`
          : score > 69 ? `C`
            : `D`).concat(
        score % 10 > 7 ? `+`
          : score % 10 < 3 ? `-`
          : ``);

const convertScoreToGradeWithPlusAndMinus3b = score =>
  (score > 100 || score < 0) ? `INVALID SCORE` : (score < 60) ? `F` : (score > 97) ? `A+` :
    (score > 89 ? `A` : score > 79 ? `B` : score > 69 ? `C` : `D`)
      .concat(score % 10 > 7 ? `+` : score % 10 < 3 ? `-` : ``);


const functionsToTest = [
  convertScoreToGradeWithPlusAndMinus1,
  convertScoreToGradeWithPlusAndMinus2,
  convertScoreToGradeWithPlusAndMinus3a,
  convertScoreToGradeWithPlusAndMinus3b
];

const passingResult = "INVALID SCORE" +
  "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" +
  "D-D-D-DDDDDD+D+" +
  "C-C-C-CCCCCC+C+" +
  "B-B-B-BBBBBB+B+" +
  "A-A-A-AAAAAA+A+A+" +
  "INVALID SCORE";

for (let functionUnderTest of functionsToTest) {
  let testResult = ``;
  for (let score = -1; score < 102; score++) {
    testResult += functionUnderTest(score);
  }
  console.log(
    functionUnderTest.name,
    testResult === passingResult ? `passed` : `FAILED: ${testResult}`
  );
}

To make it work:
if (score > 79) letter = B;
if (score > 69) letter = C;
should be:
else if (score > 79) letter = B;
else if (score > 69) letter = C;

Can you do a if else in side of another if else?

Yes. It will behave the same way, no matter how nested it is.

if (condition) {
  if (state) {

  } else {

  }
} else {

}