How to Read Nested Ternary Operators

posted by Craig Gidney on August 6, 2013

Here’s a puzzle that’s hard if you don’t know how to read the ?: operator, and relatively easy otherwise:

int i = 5;
string result = i % 2 == 0 ? "a" : i % 3 == 0 ? "b" : i % 5 == 0 ? "c" : i % 7 == 0 ? "d" : "e";
// what is the value of result?

The answer is the same in C, C++, C#, and Java.

Step 1: Fix Formatting

First things first: the above code is terribly formatted. It reads like a run-on sentence, instead of exposing the underlying structure.

I actually wrote the code as a run-on sentence on purpose because, in my opinion, getting the formatting right is basically the same thing as solving the puzzle. Properly formatted code makes the correct interpretation obvious. I almost feel like I’m giving it away, formatting it for you:

int i = 5;
string result = i % 2 == 0 ? "a"
              : i % 3 == 0 ? "b"
              : i % 5 == 0 ? "c"
              : i % 7 == 0 ? "d"
              : "e";

(Here’s a controversial statement: I consider the above formatted code to qualify as “very readable”.)

Step 2: As If

The trick to reading nested conditional (“ternary”) expressions is to read them as if-then-else expressions. If-then-else expressions, if they existed, would work exactly like if-statements except they would choose between expressions instead of statements.

Translating the puzzle into if-then-else expressions should make the answer dead obvious:

int i = 5;
string result = if i % 2 == 0 then "a"
                else if i % 3 == 0 then "b"
                else if i % 5 == 0 then "c"
                else if i % 7 == 0 then "d"
                else "e";

If that’s still not clear, here’s the same thing as if-statements:

int i = 5;
string result;
if (i % 2 == 0) {
    result = "a";
} else if (i % 3 == 0) {
    result = "b";
} else if (i % 5 == 0) {
    result = "c";
} else if (i % 7 == 0) {
    result = "d";
} else {
    result = "e";
}

Right, the solution is “c”.

First the “a” branch is not taken because 5 is not a multiple of 2. Then the “b” branch is not taken since 5 is not a multiple of 3. Then the “c” branch is chosen because 5 is a multiple 5, making the result “c”. This reasoning works for both the if-statements variant, the if-then-else variant, and the conditional operator variant.

Examples

Trivial:

i % 2 == 0 ? "even" : "odd";

// Translated:
if i % 2 == 0 then "even" else "odd";

Nesting inside the else:

i % 2 == 0 ? "even" : i < 0 ? "nodd" : "podd";

// Formatted:
i % 2 == 0 ? "even"
: i < 0 ? "nodd"
: "podd";

// Translated:
if i % 2 == 0 then "even"
else if i < 0 then "nodd"
else "podd";

Nesting inside the then:

i % 2 == 0 ? i % 4 == 0 ? "even steven" : "even" : "odd";

// Formatted:
i % 2 == 0 ? 
    i % 4 == 0 ? "even steven"
    : "even"
: "odd";

// Translated:
if i % 2 == 0 then
    if i % 4 == 0 then "even steven"
    else "even"
else "odd";

Fizz buzz, nesting inside both:

i % 2 == 0 ? i % 3 == 0 ? "fizzbuzz" : "fizz" : i % 3 == 0 ? "buzz" : i.ToString();

// Formatted (note: This case is too complicated. You won't get hired if you write it like this.):
i % 2 == 0 ?
    i % 3 == 0 ? "fizzbuzz"
    : "fizz"
: i % 3 == 0 ? "buzz"
: i.ToString();

// Translated:
if i % 3 == 0 then
    if i % 5 == 0 then "fizzbuzz"
    else "fizz"
else if i % 5 == 0 then "buzz"
else i.ToString();

I hope those were helpful, and made you wish we had if-then-else expressions instead of the conditional operator.

Summary

Read conditional operators as a sequence of if/else cases. That is to say, read "a ? b : c" as "if a then b else c". This is especially helpful when there's nesting, because the leading "if" disambiguates the meaning.

Be wary of writing nested conditional operators, even when they make a nice if-elseif-elseif-...-else pattern. You're assuming readers will know how to read conditional operators, which they might not, and so they may consider your code unreadable. (Deciding on what level of knowledge is required of readers, and permitted of writers, is kind of a difficult social problem.)

Next level of difficulty: suppress everything you just learned, and figure out how PHP horses everything up by choosing "d" instead of "c".

---

Discuss on Reddit

---

My Twitter: @CraigGidney

---

  • Eric

    Fizz buzz, nesting inside both ‘//Translated’ should be (typo?):

    if i % 2 == 0 then
    if i % 3 == 0 then “fizzbuzz”
    else “fizz”
    else if i % 3 == 0 then “buzz”
    else i.ToString();


Twisted Oak Studios offers consulting and development on high-tech interactive projects. Check out our portfolio, or Give us a shout if you have anything you think some really rad engineers should help you with.

Archive

More interesting posts (5 of 11 articles)

Or check out our Portfolio.