I realized that there is a very clean way to express a multi-clause if statement by composing ternary conditional operators like this:

var result = 
    condition1 ? result1
    : condition2 ? result2
    : condition3 ? result4
          ...
    : conditionN ? resultN
    : default;

Traditionally, this would be written in a much more verbose way:

MyType result;
if (condition1) result = result1; 
else if (condition2) result = result2;
else if (condition3) result = result3;
   ...
else if (conditionN) result = resultN;
else result = default;

Here is a simple real-world application of this trick:

string commentCount =
    n == 0 ? "no comments"
    : n == 1 ? "1 comment"
    : n < 100 ? n + " comments"
    : "100+ comments";

I really like this pattern because the code is very concise and clean. I am surprised that I have never seen it used anywhere.

From the readers

Hugh Brown suggests an alternative way to rewrite the above code sample, which also nests conditional expressions:

string commentCount =
    string.Format("{0} comment%s",
        (n == 0 ? "no"
        : n < 100 ? n.ToString()
        : "100+"),
        (n == 1 ? "" : "s"));

Gotchas

Suprisingly, I don’t believe there are any major ones. The conditional operator has a very low operator precedence in C#, Java and C++. In C# and Java, only the assignment operators (=, +=, <<=, etc) have a lower precedence than the conditional. In C++, you also have to be cautious around the comma operator, but you should be using that construct rarely anyways.

If you really want to mix the switch expression with assignment operators, other conditionals, or even the C++ comma operator, use brackets to ensure that the conditional operators which are part of the switch expression will be applied last.

In all other cases, the pattern should behave as you’d expect.

Comments and Conclusion

It is great to find a neat trick in the good old C-based languages. Not only functional languages are cool. ;-)

Any thoughts? Has anyone seen this pattern before? Let me know in the comments.

From the readers

Bodaniel Jeanes mentions a loosely-related trick with a switch statement. Note that this works in C, but not in C# or Java:

switch (true)
{
    case n == 0:
        // do something
        break;
    case n > 2:
        // do something else
        break;
    default:
        return;
}

kick it on DotNetKicks.com

Tags:

22 Comments to “A neat way to express multi-clause if statements in C-based languages”

  1. Ivan Suhinin says:

    Nice feature :). Though it’s not usable for multiline actions. Actually it’s a self-made implementation of conditional switch that is present in most functional languages :D

  2. Karl Agius says:

    It’s certainly looks nicer than using if … else … else … etc. :) Where possible. I’d really want to use a switch statement instead.

    You might also want to look at Fowler’s refactoring catalog at http://www.refactoring.com/catalog/ – particularly http://www.refactoring.com/cat.....phism.html

    If you’re working on a largish system, that’s the best way to go.

  3. This construct is less powerful than a switch statement because each branch must be a single expression. But, it is more powerful in another respect because the condition is an arbitrary boolean expression, rather than just a value to compare against.

    As always, different constructs fit different scenarios. The same goes for the polymorphism solution suggested by Karl.

  4. Adrian Burd says:

    Personally I would stick with the if/else structure even for single line statements just to maintain code consistency (although I agree it is rather verbose).

  5. Vikas says:

    I wonder what the performance of this would be in comparison with the regular if else usage.

  6. Vikas: That is an interesting thought. I would not expect there to be a performance difference. The conditional operator does not evaluate both the *if* and the *else* part of the expression – only the one that it needs to. Since the compiler could conceivably translate the code from one implementation to the other, there is no unavoidable reason why one of the implementations should be faster. In practice, there may be performance differences depending on how a particular compiler happens to compile the code, though.

  7. Bodaniel Jeanes says:

    Igor,
    It’s not necessarily less powerful than a switch statement… A tactic i’ve used before is

    switch(true)
    {
    case n == 0:
    // do something
    break;
    case n > 2:
    // do something else
    break;
    default:
    return;
    }

    However, I can’t confirm this works in *all* C-based languages. However, the ternary tactic is nice when each condition block is setting the same variable because it allows you you to perform the assignment then test for the value to assign.

  8. Bodaniel: that is a pretty cool trick! :-)

    It does not work in C#, though. C# requires the case labels to be constants.

    Still, it is clever and I like it!

  9. grooba says:

    hmmm….that last result could not be “100+ comments”

  10. josh says:

    It’ll line up better if you move the equals sign from the assignment down to align with the colons.

  11. This is a surprisingly common pattern. I’ve seen it often with a simple return statement:

    return (n == 1) ? true : false;

    Although that example is rather silly.

  12. Hugh Brown says:

    Or maybe:

    string commentCount =
    string.Format(“{0} comment{1}”,
    (n == 0 ? “no”
    : n

  13. Hugh Brown says:

    Do I have to escape my code as HTML? Okay.

    Maybe this:

    string commentCount =
    string.Format(“{0} comment%s”,
    (n == 0 ? “no”
    : n < 100 ? n.ToString()
    : “100+”),
    (n == 1 ? “” : “s”);

    or this:

    string commentCount =
    (n == 0 ? “no”
    : n < 100 ? n.ToString()
    : “100+”) +
    ” comment” +
    (n == 1 ? “” : “s”);

  14. Harris Bhatti says:

    Clean is a relative term. :). It all boils down to the formatting really. if i were to reformat the code into something else it wouldn’t look that clean on the other hand if/elseif depending upon the formatting can be written in clean ways.

    [Admin note: Harris later corrected the following part of the comment]

    Also note that it is not necessary for a ternary statement to assign something.
    You could always do:

    (condition1) ? resultvar = resultval : ( (condition2) ? return 1 : default;

    So you can see using it that way gives you other possibilities as well and you could take the program flow in which ever direction you want instead of having to return something for each branch.

  15. Harris,

    I agree that formatting is very important for this particular pattern.

    Crazy what you can do with C++… Pretty cool. C# actually requires the clauses to be expressions, so you cannot use statements like “return 1″. I would expect Java to behave similarly.

    Igor

  16. Harris Bhatti says:

    Finally!

    I remember i posted the quote somewhere couldn’t get back to the website. CORRECTION IS IN ORDER: the sample code in my previous comment is not valid c++ contrary to my beliefs before. I was pretty sure I had used that somewhere before but obviously not as i tried using the syntax above again and well the compiler denied me a twinkie. Igor, I would appreciate it if you could delete my comment and spare some sane people from some misleading remarks and conjured up syntax :). Thank You.

  17. Thanks for coming back to clear it up, Harris. :-)

    It is also possible that some C++ compilers accept and some reject that pattern. So perhaps you really did write that code in the past.

  18. No One says:

    This form actually appears in K&R’s original C Programming language book, except they put the colon at the _end_ of the previous line, e.g.


    const char *number =
    i < 0 ? "negative" :
    i == 0 ? "zero" :
    i == 1 ? "one" :
    i == 2 ? "two" :
    "lots!" ;

  19. No One says:

    (there was specific indentation there that made it much more readable, which the “code” tag did not preserve)

  20. Fabiano PS says:

    Awesome! As a ruby programmer loved the entire post

    Just tested Ruby’s (v1.8) case-when and works the same:

    case(true)
    when 1>2
    p 1
    when 1 2

  21. Fabiano PS says:

    Sorry, missed the code tag.. again the switch from ruby:


    case(true)
    when 1>2
    p 1
    when 1<2
    p 2
    end
    #= 2

  22. […] was reading Igor Ostrovsky blog on A neat way to express multi-clause if statements in C-based languages and realized this is indeed a "neat way" in Perl as […]

Leave a Reply

You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>