Skip to main content
Ben Nadel at the jQuery Conference 2009 (Cambridge, MA) with: Bob Bonifield
Ben Nadel at the jQuery Conference 2009 (Cambridge, MA) with: Bob Bonifield

Learning ColdFusion 9: The Ternary Operator

By
Published in Comments (34)

If you've ever programmed in another language (such as Javascript), I'm sure that you've seen the Ternary Operator; it's a decision making operator that requires three operands: condition, true statement, and false statement that are combined using a question mark (?) and a colon (:):

((condition) ? trueStatement : falseStatement)

To me, the ternary operator is what the IIF() method call always wanted to be. The way it works is that the condition is evaluated. If it is true, then the true statement executed; if it is false, then the false statement executes. To get a better handle on it, take a look at this code:

<!--- Loop over true and false values to test the condition. --->
<cfloop
	index="boolean"
	list="true,false"
	delimiters=",">

	<!--- Get the ternary value. --->
	<cfset output = (boolean ? "It was True" : "It was False") />

	<!--- Debug output. --->
	Output: #output#<br />

</cfloop>

Here, we are looping over both boolean values and testing them in the ternary operator. When we run the above code, we get the following output:

Output: It was True
Output: It was False

Notice that when the condition was true, the ternary operator returned the "It was True" value, and, when the condition was false, the ternary operator returned the "It was False" value. Not only does the ternary operator determine conditional execution, it also returns the resultant value. If this is confusing at all, you can think of the ternary operator as a short-hand for an IF/ELSE statement. For example the above code, could have been written long-hand like this:

<!--- Test condition. --->
<cfif boolean>

	<!--- Execute true statement and store in output. --->
	<cfset output = "It was True" />

<cfelse>

	<!--- Execute false statement and store in output. --->
	<cfset output = "It was False" />

</cfif>

As with many short-hand notations, the ternary operator is easier write, but harder to read than it's long-hand counterpart. This becomes even more true when you start to nest ternary operators within each other. In the following example, we are going to use the ternary operator to create a whole decision tree:

<!--- Define post-date action method. --->
<cffunction name="kiss">
	<!--- Woman in question performs random reaction. --->
	<cfreturn ((randRange( 1, 2 ) EQ 2) ? "Kiss back" : "Slap") />
</cffunction>

<!--- Define post-date action method. --->
<cffunction name="hug">
	<!--- Woman in question performs random reaction. --->
	<cfreturn ((randRange( 1, 4 ) GT 1) ? "Hug back" : "Kiss back") />
</cffunction>


<!--- Loop over a number of possible post-date scenarios. --->
<cfloop
	index="scenarioIndex"
	from="1"
	to="7"
	step="1">

	<!--- Pick a random date quality for action logic. --->
	<cfset dateQuality = randRange( 1, 10 ) />

	<!---
		Try an action using the following logic tree and and see
		what happens:

		1. If date quality was estimated at being 7 or better then
			A. Kiss her.
			B. If She kisses back then
				i. Kiss her again
			C. If she slaps you then
				i. Leave ashamed
		2. If date quality was estimated at being worse then
			A. Just hug her (it's cool being friends)
	--->
	<cfset result = (
		(dateQuality GT 7) ?
		(
			(kiss() EQ "Kiss back") ?
			kiss() :
			"Leave ashamed"
		) :
		hug()
		) />

	<!--- Output the action result. --->
	Result: #result#<br />

</cfloop>

In this example, we put ourselves in the shoes of a man who finds himself at the end of a date with a lovely woman and now he need to figure out how leave things. Does he hug her? Does he dare go in for the kiss? In the decision tree, which is created using nested ternary operations, he goes in for the kiss, and if kissed back, he goes back in for yet another kiss! Notice, also, that the woman's reactions to the man are determined using a random ternary operator as well (NOTE: I'm not saying that women act completely randomly, I'm just making the example more dynamic). When we run the above code, we get the following output:

Result: Hug back
Result: Leave ashamed
Result: Kiss back
Result: Hug back
Result: Slap
Result: Hug back
Result: Hug back

This code works fine, but I'd bet that you find the nested ternary operators a bit unruly. Perhaps nesting ternary operators is not the best approach when you are worried about code maintenance.

Up till now, we've only seen the ternary operator as part of a stand-alone statement; but, just as with any ColdFusion expression, ternary operators can also be executed within strings and other forms of output. In this example, we will use a ternary operator to help formulate a welcome message:

<!--- Create small user object. --->
<cfset user = {
	name = "Tricia",
	gender = "F"
	} />

<!--- Create greeting message. --->
<cfset message = "Good morning #((user.gender EQ 'F') ? 'Mrs' : 'Mr')# #user.name#" />

<!--- Output message. --->
#message#

Notice that we are dynamically including "Mr" or "Mrs" in the welcome message based on the user's gender. When we run the above code, we get the following output:

Good morning Mrs Tricia

Earlier on, I said that the ternary operator was what the IIF() method always wanted to be. I won't sugar coat it - I hated the IIF() method. I hated it, not because it didn't work (it did) - I hated it because it always disappointed me; every time I thought it might solve a problem gracefully, it ended up being too ugly to allow me to respect myself or my code. The problem I had with it lay in the delayed evaluation of its arguments. Because all arguments to a method are necessarily evaluated before the method call is actually made, we had to use the DE() method to make sure that our expressions weren't executed until after the condition was evaluated behind the scenes. This always just made me feel dirty.

With the ternary operator, however, we finally get IIF() functionality without the skeezy feeling of delayed evaluation because the ternary operator is truly conditional execution. This means that if the condition is true then only the true statement executes, leaving the false statement untouched. To demonstrate this, take a look at the following code:

<!--- Method to increment count variable. --->
<cffunction name="incrementCount">
	<cfreturn ++count />
</cffunction>


<!--- Create initial count. --->
<cfset count = 0 />

<!---
	Unlike the IIF() method call, the ternary operator is
	short-circuited so the second incrementCount() method
	call will never execute.
--->
<cfset (true ? incrementCount() : incrementCount()) />

<!--- Output current count. --->
Count: #count#

Here, both the true and false expressions consist of a call to the incrementCount() method. If this were an IIF() method call without delayed evaluation:

IIF( true, incrementCount(), incrementCount() )

... what we'd end up with is two executed calls to the incrementCount() method. However, because the ternary operator is true conditional execution, when we run our code, we get the following output:

Count: 1

The incrementCount() method was only called once. The "false" expression, while defined as a method call itself, was never evaluated. This is pretty darn awesome!

To end the topic of ternary operators, I'll provide you with one more common use case. Ternary operators, especially in languages that provide flexible method signatures, are often used to conditionally transform or default method arguments. For example, they might be used to provide a default value for an argument if none was passed in. In my demonstration, I'm going to assume a scenario in which we only want to work with arrays, but will accept incoming data that is of either data type string or array:

<!--- Create raw data. --->
<cfset data = "Tricia" />

<!---
	We want all of our data to be referenced as an array.
	If its not in array format already, make it so.
--->
<cfset data = (isArray( data ) ? data : [ data ]) />

<!--- Output new data value. --->
<cfdump
	var="#data#"
	label="Data: Converted Raw Data"
	/>

Notice that our initial data value is a string. In our ternary operator, we test the type of the data variable. If it's already an array, we simply return it (storing it back into itself). If it is a string, however, we return a new implicit array containing the data value, thereby converting the data variable from a string into an array of itself. When we run the above code, we get the following CFDump output:

ColdFusion 9's Ternary Operator Can Be Used To Transform Or Default Method Arguments.

Ternary operators are awesome and I'm super excited that ColdFusion 9 has finally introduced them. However, as with all short-hand notations, you walk the fine line between ease of use and ease of maintenance. Short-hand notations, ternary operators especially, are harder to read than their long-hand counterparts. As such, keep in mind as you write your code that you only want to use them when they are both useful and understandable.

Want to use code from this post? Check out the license.

Reader Comments

218 Comments

I honestly didn't think it'd be that useful, but I have been using ternary operators in javascript and Railo more and more. I always have to correct myself at work (CF8) when I try to use it. Glad they finally included it in CF9.

15,902 Comments

@Todd,

Yeah, sometimes it's not always easy to come up with the best use cases; but, when they get used, it always feels so good.

11 Comments

<blockquote>...every time I thought [IIF()] might solve a problem gracefully, it ended up being too ugly to allow me to respect myself or my code.</blockquote>

Man, I totally agree! It's nice when my hunches are confirmed by an expert. Thanks for the peek into CF9 Ben!

15,902 Comments

@Doug,

Sorry about that - my commenting is still being smoothed out.

That said, yeah IIF() is junk! It's just a hacky solution to a problem that ternary operators were designed to solve.

29 Comments

I only discovered ternary operators last year (i guess that's what happens when you're stuck writing ONLY CF/HTML for 8 years straight) while compiling coding standards for my company. I came across them while researching ActionScript standards, and have been using them ever since! I've even gone back and re-written a bunch of if/else statements. Nice to see CF joining the ternary party.

1 Comments

Nice post Ben, thanks.
Getting rid of the DE() alone in IIF() is amazing. Any idea if the ternary operator suffers from same performance issues that IIF did?

113 Comments

The ternary operator and short-circuiting boolean operators are very powerful and very useful.

They are seen very often in expression-oriented languages such as Haskell or JavaScript (when used in an expression-oriented way), but seems counter to the way procedural-bound programs look that people tend to write code in languages such as ColdFusion or JavaScript (when used in a procedural way).

In fact, in Haskell, the 'if-then-else' construct is actually just the ternary operator. There is no 'if(condition) then(statement1) else(statement2)' construct in Haskell - just a 'if(condition) then(expression1) else(expression2)', where expression1 and expression2 are evaluated only as necessary - just the way the ternary operator in languages such as C, C#, JavaScript, and now ColdFusion work.

The whole field of software development is currently inching towards expression-oriented programming from statement-oriented programming. In more languages, such as Haskell and C#, programmers are starting to write more, larger expressions and fewer statements. When you are working in a language or a library that makes expression-oriented programming easier, the subtle transition to more expression-oriented programming and less statement-oriented programming begins to seem natural. Witness the transformation of the JavaScript language, or rather the transformation of the way people tend to write programs in JavaScript, induced by libraries such as jQuery and Prototype.

The end result is that programs can often be much easier to understand and to write when written in an expression-oriented style, than when written in a statement-oriented style.

15,902 Comments

@Justice,

I am not sure that I fully understand the difference between an expression and a statement? Doesn't a statement just consist of one or more statements?

Are you basically saying that expression aspect of the statement is becoming more powerful on its own?

113 Comments

@Ben,

A statement is a command to the computer to *do something*, whereas an expression is a command to the computer to *calculate something*.

Statement-oriented:

array2 = copy(array1); //expression
for(i from 1 to len(array2)) {
array2[i] = 2 * array2[i]; //do!
}

Expression-oriented:

array2 = map(times_two, array1); //expression
function times_two(x) { return 2 * x; } //expression

Statement-oriented programming involves a lot of simple assignments such as "store this value in this variable, now update this variable to a new value, now do some more updates in a loop, etc." Expression-oriented programming involves a lot of definitions of names, but those definitions never change. For example, in an expression-oriented program, once you define a variable and give it a value, you never update that variable with a new value, nor do you ever perform any modifications on that value (if the value is an array, for example, you never append more elements to that array).

From that description, it appears to the programmer more familiar with writing statement-oriented programs that statement-oriented programs are simpler to write and simpler to understand. But in fact the opposite is true. I believe this explains the rise of languages such as Ruby, Python, Haskell, C#. These languages work, in part, to making programs that would be too complex to write in a statement-oriented way simple to write in an expression-oriented way, rather than making it easy for the novice to write simple programs. The recent release of C# 3, for example - you may have heard the buzzword LINQ thrown around - is the progression of the C# language in exactly this direction.

Expression-oriented programs, when written in languages without expression-oriented support, are often harder to write and understand than statement-oriented programs. This is the case of ColdFusion today. It appears that Adobe is beginning to follow the trend.

By the way, regarding your complaint about the buggy way that array/struct notation work in ColdFusion 9 Beta. You want the notation to work in an expression-oriented way, while currently it works in a statement-oriented way. Hopefully this will be fixed.

53 Comments

@Ben

Previously in your implicit struct and array usage post you mentioned that there were issues with reassigning a variable with a reference to itself inside a struct/array. However this seems not to be the case with your ternary example:

data = (isArray(data) ? data : [data])

Is the ternary evaluated right side first or did I misunderstand the reassignment referencing issue?

12 Comments

Nice post,

I love tennary, missed it too long.

A couple things to try perhaps.

1. Some code examples using script, which I know are identical almost.
2. Can tennary be embedded

ie

a = b eq 1 ? 2 : b eq 2 ? 3 : 0

might be easier to read in brackets

a = (b eq 1) ? 2 : (b eq 2) ? 3 : 0

113 Comments

@Dale, one of the styles I use for chained ternary operators is something like:

result =
cond1 ? expr1 :
cond2 ? expr2 :
cond3 ? expr3 :
exprk;

Another is:

result =
cond1
? very(long) + if(expr) * with(multiple(nested(exprs))))
: another(super(long), expr) + with(too, many) - nested(exprs);

But there is no need, IMHO, for enclosing the condition in brackets. I often like to use layout and formatting to make things clear, rather than introduce additional but unnecessary symbols into my code.

15,902 Comments

@Justice,

I think I understand a bit more. Is "expression" programming basically just interacting with an layer of abstraction above statement driven programming? Take you map() example - I assume this is, in the map() definition, actually iterating over the array and mapping the values to a new array. So, if that is correct, then the map() function itself is:

1. Using statements.
2. Changing the nature of a variable by appending new values to the mapped array as it builds it.

@Andrew,

Yeah, it seems to be a work around; it probably has something to do with the fact that arrays are pass-by-value, not pass-by-reference. I think this is why the same hack does NOT work with structs.

@Dale,

Yes, they can be embedded - take a look at my "decision tree" example in this blog post; that would be an example of an embedded ternary operator.

113 Comments

@Ben,

Yes, if the map function were defined and implemented in a statement-oriented language, the map function would take an array and a function as arguments, and internally build a new array and return it.

Haskell is an expression-oriented language, and the map function in Haskell is not defined using statements per se. The function is defined in Haskell using only expressions, and permits Haskell programmers easily to write expression-oriented programs. Of course, the most primitive pieces of the Haskell language are implemented, by the compiler, using statement-oriented machine opcodes. But that is completely transparent to how *we*, as programmers, write programs.

The point is, with a language and a set of libraries leaning toward expression-oriented programming, one can better write and understand complex program. Because this is true, many traditional statement-oriented languages, such as C#, are moving in the direction of expression-oriented code. In addition, new languages are being implemented on the CLR and on the JVM in order to make these platforms very friendly to expression-oriented programming - languages such as F# or IronPython for the CLR and JRuby or Scala for the JVM.

The question isn't how do these languages and libraries work internally. Of course, they ultimately have to be compiled down to statement-oriented opcodes (whether machine or virtual-machine opcodes). We already know that. The question is, can we use them effectively to build complex programs, and are they better for building complex systems than statement-oriented languages?

15,902 Comments

@Justice,

There's still something I'm not quite getting, sorry. For example, if I'm in jQuery and I use their each method:

$.each(
. . . . myArray,
. . . . function( index, value ){
. . . . . . . . // do something
. . . . }
. . . . );

... is this an expression or a statement? I am confused.

113 Comments

@Ben, that's a great example. In a sense, it's sort of a mix of both. JavaScript easily allows you to write programs in a statement-oriented style as well as in an expression-oriented style, even in a mix of both. Libraries such as jQuery attempt to guide the programmer to using a little bit more of an expression-oriented style. Such libraries make it easier to write more expressions (get me this result and put it in a new variable) and fewer statements (do this action, do that action, stuff this new value into this variable, update that variable).

Ultimately, this example, requests that jQuery perform an action on each value in the given array, rather than requesting that jQuery calculate a complex result and return it. But, this example is a little more expression-oriented and a little less statement-oriented than the for-loop of native JavaScript, because the jQuery each method doesn't involve repeatedly updating the index and value variables - these are arguments and are set once (per invocation of the action function) using the each method, rather than being variables and being set multiple times (once per iteration of the loop) using the native JavaScript for loop.

The best example of an expression-oriented program that you would be familiar with, Ben, is any SQL SELECT *query* (not SQL statements, but just queries). The more complex the statement, the more JOINs and WHEREs and GROUPs and ORDERs, the more subqueries, the better the example. When you start looking at a complex requirement and try to implement it with multiple temporary tables and many inserts/updates/deletes to the temporary tables and relatively simple queries, that's statement-oriented. When you look at the same complex requirement and try to implement it using powerful query operators - joins, filters, groups, subqueries, projections, etc. - that's expression-oriented.

To answer your question directly, I would like to ask you a question: are you writing more expressions (calculations, assignments to new variables only, complex structured data) or are you using more statements (re-assignments to previously assigned variables) in your code? It appears to me that you are writing slightly more expressions and slightly fewer statements.

Expressions are an abstraction over statements (just like loops are an abstraction over gotos) and provide the programmer a more powerful, yet simpler, way of writing programs. (The question at hand is, how do *you* write programs, not how is this nice expression implemented under the hood.) Languages and libraries are tending to move in this direction, some slowly, some quickly, but you can be sure that it's happening.

Please don't take what I'm saying to mean that "you should always use the jQuery each method instead of using native JavaScript for-loops." Because, as we all know, if we went and replaced all our for-loops with each method calls, that could easily make some code clearer and other code less clear - it depends on the code in question! So I would say that reading my words "languages are moving, on the whole, in the direction of making it easier for the programmer to write expression-oriented code" as "always use the jQuery each method instead of the JavaScript for-loop" would be fairly dumb.

What I do want to say is that, oftentimes (not necessarily most of the time, certainly not always), it helps to write your programs in a more expression-oriented style. Therefore, programming languages and libraries are moving in a direction to help the programmer do that, when the programmer wants to. It's up to you the programmer to determine when it's appropriate.

Personally, I much prefer expression-oriented programming. I am very much a fan of Haskell, C#, Ruby, and Python, for example, for making expression-oriented programming either the default or at least much easier - because that's my preferred style of programming.

15,902 Comments

@Justice,

Hmmm, perhaps I write more expressions than statements? I really am not sure - sorry, there is just a mental block where I'm not quite seeing the difference. For instance, the CFLoop tag in ColdFusion 8 can implicitly loop over an array in such a way that I dont see the CFSets taking place, but I know they are. Of course, I use the same variable to hold the iteration value, so I guess that's a statement.

113 Comments

@Ben, CFLoop seems to me very much a statement, not an expression. Each iteration of the loop updates the iteration variable. CFLoop is very similar to the for-loop in CFScript, JavaScript, and many other languages.

CFLoop is not an implicit loop, nor are the variable-assignments implicit. They pervade the way you then write the code within the loop. And even though you don't write the variable-assignment directly, in the normal way that you write other variable-assignments, you are most certainly writing a variable assignment in CFLoop.

Take a look.

<cfloop index="VARIABLES.indexVariable" array="#VARIABLES.longArray#"></cfloop>

It is perfectly clear that every iteration of the loop, for each element in VARIABLES.longArray, causes the variable VARIABLES.indexVariable to be updated. You the programmer are assigning the variable VARIABLES.indexVariable on every iteration - yes, it's part of the CFLoop construct, but it's there. CFLoop is the direct analog of the for-loop in CFScript, and there's no getting around that.

In addition, note that CFLoop is not a way of computing a result based on an input array. It is a way of performing actions based on an input array. Using CFLoop always means that *you the programmer* will then be using statements within the body of the CFLoop.

Take the example of the "map" function many posts ago:
resultArray = map(inputArray, function(x) { return 2 * x; });
resultArray2 = filter(resultArray, function(x) { return x < 100; });
You will note that in this example, there are only *two* assignments total, and that that all assignments are to *new* variables. In this example, we are not going through an array and calling an action function on each element - we are going through the array and calculating a result from each element.

The statement-oriented variation of the previous example would look like this:

var i = 0;
var resultArray = [ ];
var resultArray2 = [ ];

for(i = 1; i <= ArrayLen(inputArray); i += 1)
ArrayAppend(resultArray, 2 * inputArray[i]);

for(i = 1; i <= ArrayLen(resultArray); i += 1)
if(resultArray[i] < 100)
ArrayAppend(resultArray2, resultArray[i]);

As you can see, in this example requirement, writing an expression-oriented program makes the code simple, while writing a statement-oriented program makes the code less simple. The expression-oriented program is more about "what is the result I want to achieve, expressed in high-level terms?" while the statement-oriented program is more about "which bits do I need to flip? - and oh by the way you will have to work to infer the high-level description of what this is supposed to do, because no-one is telling you".

Once again, some code is better written in an expression-oriented style, while some code is better written in a statement-oriented style. It depends on a lot of things. The ultimate point is, because languages and libraries are offering more and more support for writing programs in a more expression-oriented way, it is becoming easier and more natural for us to write programs in an expression-oriented way, when the programs actually deserve to be written that way in the first place, by moving to new versions of the same language or by moving to new languages. Again, I'm not saying "write more expressions" - I'm saying, "expressions can be very clear and very helpful, and new languages or new versions of languages are helping you write them."

I would say that a large part of the mental block is that you haven't had much experience in the past with highly expression-oriented languages such as ML or Haskell (these languages tend to make statement-oriented programs much harder and expression-oriented programs much easier to read and write). So it is certainly difficult to understand all the nuances of a new or foreign concept without having had exposure to it.

I will say that Haskell and Ruby are the two languages to watch and even to learn, because they represent the future of mainstream programming languages (not that we will all necessarily be using these particular languages per se). Microsoft *explicitly* has spent the past four years making C#, its flagship language and its clone of C++ and Java, more and more Haskell-like and Ruby-like. Microsoft knows that that's where languages are moving, so it had better keep up.

15,902 Comments

@Justice,

I think I like using expression, from what I can gather, but they still only seem like layers of abstraction. Even look at the ternary operator - the original topic of the post - it's still just a layer of removal from an if/else statement. I'm not saying that as a bad thing - many of the great programming features I enjoy in ColdFusion are layers of removal from the nitty gritty.

7 Comments

I didn't take the time to read all of the comments that others made, so you might already know this. Not that it matters because the article is very well written. Your first test at the top,

# <cfloop
# index="boolean"
# list="true,false"
# delimiters=",">
#
# <!--- Get the ternary value. --->
# <cfset output = (boolean ? "It was True" : "It was False") />
#
# <!--- Debug output. --->
# Output: #output#<br />
#
# </cfloop>

Both true and false are boolean. They would both have resulted in "It was True"

Thank you for the article. :)

53 Comments

@Hugh,

In the ternary, boolean is a variable, not a data type. So when Coldfusion loops over the list and assigns boolean with a value; it will do an implicit conversion of "true" and "false" giving you "It was True" and "It was False".

2 Comments

Thanks Ben, came across this post today while looking for a shorthand method for cfif, this is very useful with language switching sites.

15,902 Comments

@Thomas,

My pleasure. This kind of construct is new in ColdFusion 9, but is definitely something that exists in a number of other languages.

1 Comments

>>it's long-hand counterpart

Your posts are always helpful, so I don't like to find fault. However, the word 'it's' means 'it is' or 'it has', *not* 'of it'. You are confusing it with the word 'its'.

15,902 Comments

@Mark,

No worries :) The one that *always* trips me up is: who's vs. whose. The thing I have to remember, I think, is that the apostrophe ALWAYS means concatenation.... I think :D

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel