Elliot Bonneville
Elliot Bonneville

Reputation: 53331

A somewhat painful triple-nested ternary operator

I went looking through Raphael.js's source code to find out how he converted RGB values to HSB. I found out the function he did it in and I was in the process of converting it to Python when I bumped into this nice triple-nested ternary operator:

H = (C == 0 ? null :
    V == r ? (g - b) / C :
    V == g ? (b - r) / C + 2 :
             (r - g) / C + 4
    );

It threw me for a loop because Python doesn't have the same kind of ternary operator that Javascript does. I spent a while looking over it and eventually hashed this somewhat saner code (using only if/else) out of it:

if (C == 0) {
    H = null;
} else {
    if(V == r) {
        H = (g - b) / C;
    } else {
        if(V == g) {
            H = (b - r) / C + 2;
        } else {
            H = (r - g) / C + 4;
        }
    }
}

Was my interpretation correct? I'm only asking this because if it isn't correct, I'm faced with a lot of debugging. So. Did I "get it"?

Upvotes: 63

Views: 135562

Answers (10)

I tend to avoid nested if/else (even more nested ternary operators ? : ) statements as I find them hard to understand when I'm reading code.

One way to avoid nesting is to use early returns on functions. To do this we use a lambda.

const H = (() =>
  if(C == 0) {
    return null;
  }

  if(V == r) {
    return (g - b) / C;
  }

  if(V == g){
    return (b - r) / C + 2;
  } 

  return (r - g) / C + 4;
)();

Upvotes: 0

To my personal taste, a carefully aligned nested ternary beats the if-else mess:

const H =
  C == 0 ? null            :
  V == r ? (g - b) / C     :
  V == g ? (b - r) / C + 2 :
           (r - g) / C + 4 ;

Upvotes: 55

Rarity Belle
Rarity Belle

Reputation: 58

Why not use ternary operators found in Python?

H = (
    None            if C == 0 else
    (g - b) / C     if V == r else
    (b - r) / C + 2 if V == g else
    (r - g) / C + 4
)

Upvotes: 0

Shivam Jha
Shivam Jha

Reputation: 4522

As mentioned in MDN Docs:

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; }
}

Upvotes: 3

Lars Gyrup Brink Nielsen
Lars Gyrup Brink Nielsen

Reputation: 4105

If your JavaScript codebase contains nested ternary statements like the one in question, consider converting the formatting to daisy chained ternary statements instead.

H = (C == 0)           // Is C zero?
    ? null             // Then return `null`, else ...
    : (V == r)         // Is V equal to r?
    ? (g - b) / C      // Then return this value, else ...
    : (V == g)         // Is V equal to g?
    ? (b - r) / C + 2  // Then return this value
    : (r - g) / C + 4; // Otherwise fall back to this default value

They simply read top to bottom in a straight line, returning a value as soon as they hit a truthy condition or the fallback.

Nested Ternaries are Great, Eric Elliot

Upvotes: 12

onedevteam.com
onedevteam.com

Reputation: 4178

Here's another, more elegant idea...

if (C != 0) 
{
  if (V == r) return (g - b) / C;
  if (V == g) return (b - r) / C + 2;
  return (r - g) / C + 4;
}

return null;

Just wrap this in function and use instead of H...

Upvotes: 1

ssomnoremac
ssomnoremac

Reputation: 759

H = C == 0 
    ? null 
    : V == r 
        ? (g - b) / C 
        : V == g 
            ? (b - r) / C + 2 
            : (r - g) / C + 4

I've seen Dan Abramov using this indentation placement pattern. While I don't like how the conditional operator ? no longer visually follows the condition, I prefer this to something like @lolmaus's example in that the indentation will always be consistent regardless the size of the conditional.

You actually start to look at it as ? true : false which is visually intuitive here. And this way, I find the ternary is much easier to spot and differentiate from the surrounding code.

Upvotes: 7

Bergi
Bergi

Reputation: 664969

Yes, it's right (apart from capitalisation differences). Yet, it may be cleaner written without any parentheses, readable as elseif:

if (C == 0)
    h = null;
else if (V == r)
    h = (g - b) / C;
else if (V == g)
    h = (b - r) / C + 2;
else
    h = (r - g) / C + 4;

Upvotes: 3

Óscar López
Óscar López

Reputation: 236114

The same logic can be written in a simpler way:

var H

if (C == 0)
    H = null;
else if (V == r)
    H = (g - b) / C;
else if (V == g)
    H = (b - r) / C + 2;
else
    H = (r - g) / C + 4;

It's possible to omit the curly braces because there's a single statement in each condition. And given that the conditions are mutually exclusive, using else if is much clearer than nesting ifs.

Upvotes: 6

Joseph
Joseph

Reputation: 119867

I think you can have this to avoid the deep nesting:

var H

if(C == 0){
    H = null;
}
else if(V == r){
    H = (g - b) / C;
}
else if (V == g){
    H = (b - r) / C + 2;
}
else {
    H = (r - g) / C + 4;
}

Upvotes: 42

Related Questions