loading...
Cover image for How Ternaries Can Improve Your JavaScript Conditionals

How Ternaries Can Improve Your JavaScript Conditionals

codeartistryio profile image Reed Barger Originally published at reedbarger.com ・8 min read

If-else statements in JavaScript are great for what's known as flow control, in other words, for establishing what happens in our application based on one condition or another.

If-else statements are very easy to read and understand what is happening

For example, say we are making an e-commerce app where users can add items to a cart. In it, we are authenticating our users.

First, we could check to see if we have an authenticated user, and if so, enable them to add an item to their cart.

Else, if they are not authenticated, we have them do something else entirely, like tell the user to login.

const isAuthenticated = false;
let cartItemCount = 0;

if (isAuthenticated) {
  // add item to cart
  cartItemCount = 1;
} else {
  // tell user to login
  console.log("Please log in!");
}

Use ternaries

If a user is authenticated, we will set cartItemCount to 1, otherwise, it remains at 0:

const isAuthenticated = false;
let cartItemCount = 0;

if (isAuthenticated) {
  // add item to cart
  cartItemCount = 1;
} else {
  // tell user to login
  // console.log("Please log in!");
  cartItemCount = 0;
}

Notice the repetition—we have to repeat ourselves by saying cartItemCount two or more times after we declare it as a variable.

Try to imagine how much repetition we would have if we needed to conditionally set multiple variables.

In programming, a large aim is to avoid needlessly repeating ourselves. If you find yourself having to repeat the same operation many times, over and over, try to look for alternative. There's usually a better pattern to follow.

Fortunately there is a better way to do this—to conditionally set the value of a variable without repeating ourselves and by using less code. We achieve both of these ends by using an operator called a ternary operator.

The Question Mark (?)

We can simplify our code by replacing the if part along with it's parentheses () with the value we want to apply the condition to (in this case isAuthenticated), and afterwards, add a ?

const isAuthenticated = false;
// let cartItemCount = 0;
// if (isAuthenticated) {
//   // add item to cart
//   cartItemCount = 1;
// } else {
//   // tell user to login
//   // alert("Please log in!");
//   cartItemCount = 0;
// }

isAuthenticated ?
// if «condition»

Looking back at our previous code, when isAuthenticated was true, cartItemCount was updated to 1.

Here's the special thing about ternaries in relation to variables—we don't have to immediately assign our variables.

We don't have to say if isAuthenticated is true, set cartItemCount is equal to 1:

isAuthenticated ? cartItemCount = 1 // unnecessary assignment

Instead, we can remove the reference to the variable in this part, called the then part of the conditional, and instead move it to the beginning:

const cartItemCount = isAuthenticated ? 1
// «condition» ? «then»

// if isAuthenticated is true, 1 is put in cartItemCount

Ternaries are expressions, unlike if statements

This is because a ternary is an expression, instead of a statement. By definition, all expressions in JavaScript resolve to a value.

What does this mean?

If our ternary runs, and isAuthenticated is true, the then part is executed and returns the value 1. It resolves to the value 1, and therefore can be immediately put in our cartItemCount variable.

This is a great benefit: ternaries allow us to reference the variable we are conditionally setting just once, when the variable is declared.

The Colon (:)

But what about the else condition, if isAuthenticated is false?

This part of the conditional is put after another special symbol, the colon:

const cartItemCount = isAuthenticated ? 1 :
// «condition» ? «then» : «else»

After the colon is where we jump to if our ternary's condition resolves to false.

Looking at our if statement from before, in the else part, we were setting cartItemCount to 0.

Since we now know that ternaries returns the value they resolve to, we can just put 0 in our else condition and it will immediately be put in our cartItemCount variable as well:

const cartItemCount = isAuthenticated ? 1 : 0;

Finally, let's console log cartItemCount and see what we get. But before we do, if isAuthenticated is set to true, what do you think the value of cartItemCount will be?

const isAuthenticated = true;
const cartItemCount = isAuthenticated ? 1 : 0;

console.log(cartItemCount); // 1

We get 1.

isAuthenticated resolves to true in our condition, therefore the then condition runs and 1 is implicitly returned and assigned to cartItemCount.

And if we update isAuthenticated to false:

const isAuthenticated = false;
const cartItemCount = isAuthenticated ? 1 : 0;

console.log(cartItemCount); // 0

We get 0.

isAuthenticated is false in our condition, then the else condition runs and 0 is put in cartItemCount.

Use expressions in ternaries, don't perform actions

And finally, what if instead of just returning a value, we also wanted to do something like we did before, where we were alerting the user to login?

const isAuthenticated = false;
const cartItemCount = isAuthenticated ? 1 : console.log("Please log in");

console.log(cartItemCount);
// Please log in
// undefined

We see our alert, but what happens to cartItemCount? It now has the value undefined instead of 0, which is wrong.

The reason for this is that functions (which includes console.log), if they do not have a return value, return undefined by default.

Know that ternaries are very helpful in some situations, but they shouldn't be used all the time for every conditional you have to write

Ternaries help us to conditionally assign a variable without repetition, but if you want to perform multiple actions or do something that doesn't give us a return value, use a normal if statement like we had before.

A more in-depth example

Let's take a look at another example, where we want to greet a user based on their age.

Let's say we are getting their age from an input or a form, and we're putting the result in a variable called age. For our first user, it's 20:

const age = 20;

Then we want to create a custom greeting for our users based on their age.

To do that, we'll use an if statement. We'll conditionally store the greeting text in a variable called greeting.

For now, we'll just have one condition. If the age is less than 10, we'll say "Hey there". Otherwise, if they are any other age, else, we'll say "That's an interesting age"

let greeting;

if (age < 10) {
  greeting = "Hey there";
} else {
  greeting = "That's an interesting age!";
}

So now take a minute, using the if statement we have here and convert it to a ternary.

Our ternary has three parts. First we'll begin by adding our conditional, which will resolve to a boolean value, true or false. In our case, we need to see if age is less than 10. If that's true, we want the value of the greeting to be 'Hey there'. And if this condition resolves to false, the value should be 'That's an interesting age'. And we want to conditionally update the variable greeting:

const age = 20;
// let greeting;

// if (age < 10) {
// greeting = "Hey there";
// } else {
// greeting = "That's an interesting age!";
// }

const greeting = age < 10 ? "Hey there" : "That's an interesting age!";
console.log(greeting); // That's an interesting age!

The benefit here, like in the example we saw before, is that we can use const to declare our greeting variable instead of let.

Given how const and let operate, we can see that this is a big win when it comes to the reliability of our code and why we should rewrite basic conditionals to ternaries when can when conditionally assigning variables.

We know, just from looking at greeting that it cannot be reassigned and therefore it's value will not change in the future, since it was declared with const. And that's on top of being able to remove several lines of code.

Using multiple ternaries

As we know with if statements, we can chain on multiple conditions with else-if.

Say for example, if the user's age was greater than 10, we might have the greeting "What's up?", and if the user was older than 18, we could say "Greetings."

We can add both of those conditionals as else-ifs to our original if statement:

let greeting;
if (age < 10) {
  greeting = "Hey there";
} else if (age > 18) {
  greeting = "Greetings";
} else if (age > 10) {
  greeting = "What's up?";
} else {
  greeting = "That's an interesting age!";
}

What if we wanted to write the same conditions for our ternary? In fact we can, and that's by chaining multiple ternaries together.

This is a bit tricky to setup, so let's walk through this together.

For another ternary to be chained on, it is usually as the else condition of the previous ternary. So if for example, the age of a user is now 12, the else expression is run of our first ternary, and instead of returning "That's an interesting age", we want to include our next condition, and therefore our next ternary.

const age = 12;

const greeting = age < 10 ? "Hey there" : «condition»
// if the age is not 10, hit the «else» part, where the next condition will be put

Is the age (12) less than 10?

If it is, we return the text "Hey there", otherwise, the else condition of the next ternary runs.

And then we provide our next condition--is the age (12) greater than 18?

const greeting = age < 10 ? "Hey there" : age > 18 ? "Greetings" : «condition»

No, 12 is not greater than 18. So we chain on another condition.

Is 12 greater than 10?

const greeting =
  age < 10
    ? "Hey there"
    : age > 18
    ? "Greetings"
    : age > 10
    ? "What's up?"
    : "That's an interesting age";
console.log(greeting); // What's up?

Yes, so the text returned will be "What's up", else (if none of the conditions match), we'll return "That's an interesting age."

Don't chain multiple ternaries together

What do you think? Do you like ternaries better now?

No, this is tricky to reason through and write, even for an experienced JS developer.

This highlights an important aspect about ternaries:_ though you can chain multiple ternary expressions together, you should avoid doing so_.

If you're thinking this conditional is unreadable as the one who wrote it, its even more unreadable for others. Plus it loses the value of simplicity. For complex conditionals, use if statements.

This reveals an important concept about the nature of coding in JavaScript is that clarity and readability should be your focus when writing code, not how short you can make it. Sometimes think using tools like the ternary will make our code better, but sometimes it requires writing more code to make it more comprehensible.

Summary

Let's review: the basic syntax of the ternary is as follows:

«condition» ? «then expression» : «else expression»;

These three parts, condition, then, and else are why this is called a ternary. Note that the word ternary means to have three elements.

It works like this:

  • If condition is truthy, evaluate and return thenExpression.

  • Otherwise, evaluate and return elseExpression.

We also saw that it implicitly returns the value that is created from either the then or else expression. That's what makes it so great for conditionally assigning a value to a variable.

Ternary expressions allow your code to be not just more simple, but also more predictable. They do this because they cut down on the number of variables that are being reassigned.

But note that ternaries shouldn't be used everywhere, say if you need to do multiple things in a conditional such as return a value and perform an action like a console.log.

Want To Become a JS Master? Join the 2020 JS Bootcamp

Join the JS Bootcamp Course

Follow + Say Hi! 🎨 TwitterInstagramreedbarger.comcodeartistry.io

Posted on Jun 11 by:

codeartistryio profile

Reed Barger

@codeartistryio

Sharing artful coding skills that fuel the life you want to live @ CodeArtistry.io 🎨

Discussion

markdown guide
 

To me ternaries look cool, but end up making the code denser than it needs to be. Normally for stuff like


function work() {
    ....
    let cartItemCount = 0;

    if (user.isAuthenticated) {
        cartItemCount = fetchCountFromDB(user);
    } else {
        cartItemCount = 0;
    }
    ....
}

I'd just move it to a function,


function work() {
    ....
    let cartItemCount = getItemCount(user);
    ....
}

function getItemCount(user) {
    if (!user.isAuthenticated) {
        return 0;
    }
    return fetchCountFromDB(user);
}

This is a bit more code, but easier for me to come back to later.

 

That is generally good, as long as you don't slide into DRY spaghetti. If you only use it once, it seldom belongs in its own function. In your example, getItemCount() is a good candidate however, as it makes sense you may need it elsewhere.

If performance matters, you also need to be careful about instruction cache misses, which occur when calling a function in an altogether separate area of the code. These are fine in moderation, but can become a problem when you're jumping that gap hundreds or thousands of times a second. (When this starts mattering, use a proper code profiler to determine the problem.)

 

For some reason, I went years without ever writing a single ternary operator. I just didn't have them in my "mindspace" for some reason. Now, I tend to use them frequently. I specifically leverage them a lot inside JSX when I'm conditionally controlling CSS attributes - like this:

return (
  <div
    style={{backgroundColor: isActive ? 'green' : 'red'}}
  >
    All the content...
  </div>
);

However, there is one aspect of your approach that I simply can't agree with: Chained ternaries. I know that this is up for some debate, but I simply can't abide by chained ternaries. No matter how many times I see them, I never feel like they're easy for me to mentally parse.

For my own dev, I have a hard-and-fast rule: No chained ternaries.

 

I think most of the times ternary operators breaking readability of code. But some times I use them too.

For me these usages are much better.

const isAuthenticated = false;
const cartItemCount = isAuthenticated || 0;
console.log(cartItemCount); // 0

const age = 20;
if (age < 10) greeting = "x";
greeting = "y";

 

I think your absolutely right regarding “Don't chain multiple ternaries together”. In the code base I work on they allow this practice, but I really really dislike it. I think reading code should be as easy as reading a book. Comments are also underrated in my opinion. I honestly think each line of code should be editable later by a junior or senior dev alike. People think they are being smart using multiple ternaries but they are creating a form of fragile code IMO.

 

Indeed. They think they are being smart, while in fact they are being clever. And clever code is almost always bad code.

 

The only ternaries I will allow are where each component is either a single variable or literal. No method calls, no complex expressions.

Ternaries act as big multipliers on the cognitive load of expressions.

To quote Harold Abelson

Programs must be written for people to read, and only incidentally for machines to execute.

 

In my opinion it is not a matter of use one of another but what is more readable in terms of maintainability. Ternaries are really useful to set initial values , for example, setting weather someone is authenticated or not so then you can run a set of actions based on it.

 

Great article!
I love using ternaries because they make conditionals more compact and concise, plus they're expressions that evaluate to a value instead of plain statements.
And will also agree that the practice of "daisy-chaining" of ternaries doesn't look good in production code. It happened to me and were like 8 lines of unreadable code 😫