Skip to main content
Ben Nadel at the jQuery Conference 2009 (Cambridge, MA) with: John Resig
Ben Nadel at the jQuery Conference 2009 (Cambridge, MA) with: John Resig ( @jeresig )

Strange arrayContains() Behavior In For-Loop In ColdFusion 10

By on
Tags:

Yesterday, I wrote some code that used arrayContains(). Now, I don't use the arrayContains() or the arrayFind() ColdFusion methods very often. To be honest, it just doesn't come up as a use-case that often. And, when it does, I tend to shy away from these functions because I've seen them go "wrong" in the past. But, it's been years since they were introduced and I'm on a different version of ColdFusion (10), so I wanted to give them another try. In a loosely-typed language, however, understanding the interoperability between strings and numbers can be a bit of stressful guessing game. To calm my nerves, I wanted to do a little experiment. And, while I was performing this experiment, I stumbled upon a seriously odd arrayContains() behavior in the context of a for-loop.

The experiment was going to be basic: have an array of mixed-type "simple" values and then try to search for those values using mixed-type inputs. At first, I started to code individual use-cases. But, this seemed tedious; so I switched over to using a for-loop. Only, when I did that, I noticed that some of the results changed! To get a clearer picture, I ended up creating a demo that runs the same arrayContains() searches using both a for-loop and a set of hard-coded function calls:

<cfscript>

	// Build up a collection of mixed-type values. These are all "simple" values, but
	// some of them are more strongly associated with a given data type.
	values = [
		1,
		"2",
		"3.0",
		javaCast( "int", 4 ),
		javaCast( "double", 5 ),
		javaCast( "long", 6 ),
		7.0,
		javaCast( "string", "8" ),
		javaCast( "char", "9" ),
		javaCast( "float", "10.0" )
	];

	writeOutput( "<h2>#server.coldfusion.productVersion#</h2>" );
	writeOutput( "<div style='float: left ; width: 150px ;'>" );
	writeOutput( "<h3>Loop</h3>" );

	// First, let's test the arrayContains() through a loop.
	for ( i = 1 ; i <= 10 ; i++ ) {

		writeOutput( "Testing #i#: <br />" );
		writeOutput( arrayContains( values, i ) & "<br />" );
		writeOutput( arrayContains( values, "#i#" ) & "<br />" );
		writeOutput( arrayContains( values, "#i#.0" ) & "<br />" );
		writeOutput( arrayContains( values, javaCast( "int", i ) ) & "<br />" );
		writeOutput( arrayContains( values, javaCast( "float", i ) ) & "<br />" );
		writeOutput( "<br />" );

	}

	writeOutput( "</div>" );
	writeOutput( "<div style='float: left ; width: 150px ;'>" );
	writeOutput( "<h3>Hard-Coded</h3>" );

	// Now, let's run the same exact tests, but with hard-coded values.
	writeOutput( "Testing 1: <br />" );
	writeOutput( arrayContains( values, 1 ) & "<br />" );
	writeOutput( arrayContains( values, "1" ) & "<br />" );
	writeOutput( arrayContains( values, "1.0" ) & "<br />" );
	writeOutput( arrayContains( values, javaCast( "int", 1 ) ) & "<br />" );
	writeOutput( arrayContains( values, javaCast( "float", 1 ) ) & "<br />" );
	writeOutput( "<br />" );

	writeOutput( "Testing 2: <br />" );
	writeOutput( arrayContains( values, 2 ) & "<br />" );
	writeOutput( arrayContains( values, "2" ) & "<br />" );
	writeOutput( arrayContains( values, "2.0" ) & "<br />" );
	writeOutput( arrayContains( values, javaCast( "int", 2 ) ) & "<br />" );
	writeOutput( arrayContains( values, javaCast( "float", 2 ) ) & "<br />" );
	writeOutput( "<br />" );

	writeOutput( "Testing 3: <br />" );
	writeOutput( arrayContains( values, 3 ) & "<br />" );
	writeOutput( arrayContains( values, "3" ) & "<br />" );
	writeOutput( arrayContains( values, "3.0" ) & "<br />" );
	writeOutput( arrayContains( values, javaCast( "int", 3 ) ) & "<br />" );
	writeOutput( arrayContains( values, javaCast( "float", 3 ) ) & "<br />" );
	writeOutput( "<br />" );

	writeOutput( "Testing 4: <br />" );
	writeOutput( arrayContains( values, 4 ) & "<br />" );
	writeOutput( arrayContains( values, "4" ) & "<br />" );
	writeOutput( arrayContains( values, "4.0" ) & "<br />" );
	writeOutput( arrayContains( values, javaCast( "int", 4 ) ) & "<br />" );
	writeOutput( arrayContains( values, javaCast( "float", 4 ) ) & "<br />" );
	writeOutput( "<br />" );

	writeOutput( "Testing 5: <br />" );
	writeOutput( arrayContains( values, 5 ) & "<br />" );
	writeOutput( arrayContains( values, "5" ) & "<br />" );
	writeOutput( arrayContains( values, "5.0" ) & "<br />" );
	writeOutput( arrayContains( values, javaCast( "int", 5 ) ) & "<br />" );
	writeOutput( arrayContains( values, javaCast( "float", 5 ) ) & "<br />" );
	writeOutput( "<br />" );

	writeOutput( "Testing 6: <br />" );
	writeOutput( arrayContains( values, 6 ) & "<br />" );
	writeOutput( arrayContains( values, "6" ) & "<br />" );
	writeOutput( arrayContains( values, "6.0" ) & "<br />" );
	writeOutput( arrayContains( values, javaCast( "int", 6 ) ) & "<br />" );
	writeOutput( arrayContains( values, javaCast( "float", 6 ) ) & "<br />" );
	writeOutput( "<br />" );

	writeOutput( "Testing 7: <br />" );
	writeOutput( arrayContains( values, 7 ) & "<br />" );
	writeOutput( arrayContains( values, "7" ) & "<br />" );
	writeOutput( arrayContains( values, "7.0" ) & "<br />" );
	writeOutput( arrayContains( values, javaCast( "int", 7 ) ) & "<br />" );
	writeOutput( arrayContains( values, javaCast( "float", 7 ) ) & "<br />" );
	writeOutput( "<br />" );

	writeOutput( "Testing 8: <br />" );
	writeOutput( arrayContains( values, 8 ) & "<br />" );
	writeOutput( arrayContains( values, "8" ) & "<br />" );
	writeOutput( arrayContains( values, "8.0" ) & "<br />" );
	writeOutput( arrayContains( values, javaCast( "int", 8 ) ) & "<br />" );
	writeOutput( arrayContains( values, javaCast( "float", 8 ) ) & "<br />" );
	writeOutput( "<br />" );

	writeOutput( "Testing 9: <br />" );
	writeOutput( arrayContains( values, 9 ) & "<br />" );
	writeOutput( arrayContains( values, "9" ) & "<br />" );
	writeOutput( arrayContains( values, "9.0" ) & "<br />" );
	writeOutput( arrayContains( values, javaCast( "int", 9 ) ) & "<br />" );
	writeOutput( arrayContains( values, javaCast( "float", 9 ) ) & "<br />" );
	writeOutput( "<br />" );

	writeOutput( "Testing 10: <br />" );
	writeOutput( arrayContains( values, 10 ) & "<br />" );
	writeOutput( arrayContains( values, "10" ) & "<br />" );
	writeOutput( arrayContains( values, "10.0" ) & "<br />" );
	writeOutput( arrayContains( values, javaCast( "int", 10 ) ) & "<br />" );
	writeOutput( arrayContains( values, javaCast( "float", 10 ) ) & "<br />" );
	writeOutput( "<br />" );

	writeOutput( "</div>" );

</cfscript>

As you can see, the target collection contains a mixed bag of numbers, strings, and Java-cast values (which are also basically "simple" values). I'm then running a series of arrayContains() calls both in a for-loop and in a set of explicitly provided values. I then ran this code in both ColdFusion 10 and ColdFusion 11 and I get the following output:

NOTE: I've placed the outputs side-by-side for easier comparison.

arrayContains() behavior changes in ColdFusion 10 and ColdFusion 11.

First of all, let's just be clear that these function calls do seem to work for simple strings and numbers. Meaning, when I'm using vanilla "ColdFusion simple data types", like 2 and "3", the type coercion works well in the arrayContains() in both the for-loop as well as in the explicitly provided input.

But, when we start looking at javaCast() values, things get a little inconsistent. For example, look at case 4, which is javaCast( "int", 4 ) in the values collection. In the for-loop, these two return NO:

writeOutput( arrayContains( values, i ) & "<br />" );
writeOutput( arrayContains( values, "#i#" ) & "<br />" );

... but when hard-coded, these two return YES:

writeOutput( arrayContains( values, 4 ) & "<br />" );
writeOutput( arrayContains( values, "4" ) & "<br />" );

The only difference here is that the loop iterator, "i", was replaced with a hard-coded 4. I can't think of any reason why this would affect the results. Unless maybe the for-loop is using some strange data-type for the iterator.

Luckily, all of these inconsistencies seem to be fixed in ColdFusion 11. And, in fact, more of the comparisons to Java-cast values seem to work as one might expect. So, that's a win.

Languages that have automatic type coercion are interesting. On the one hand, it can make them super easy to work with in many cases. But, on the other hand, it can make value comparisons a bit of guessing game, especially when those comparisons are black-boxed behind something like arrayContains(). Unfortunately, I'm still on ColdFusion 10 in production, so I can use arrayContains(); but, will probably only feel comfortable using it in the most basic situations.

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

Reader Comments

15,674 Comments

Ugggg! I just got burned by this ... again. I'm so irritated that this doesn't just *work* as expected. I had to create an arrayContainsSafe() method. Meh.

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