Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
I am the chief technical officer at InVision App, Inc - a prototyping and collaboration platform for designers, built by designers. I also rock out in JavaScript and ColdFusion 24x7.
Meanwhile on Twitter
Loading latest tweet...
Ben Nadel at CFUNITED 2010 (Landsdown, VA) with:

Finding Values In A ColdFusion Array Using Java - And Other Cool Stuff

By Ben Nadel on
Tags: ColdFusion

If you have ever tried to find a simple value in an array, you will quickly find that ColdFusion does not have any built in ArrayFind() method. As I am quickly finding out, it is amazing that ColdFusion is built on top of Java 2 as Java has such methods that we can access directly.

Before we look at the sleek and sexy Java methods, let's take a look at how we would traditionally find a simple value in an array. There are two methods which vary in complexity and speed. The simplest is to convert to a list:

  • <!--- Get index based on list find. --->
  • <cfset intIndex = ListFind(
  • ArrayToList( arrValues ),
  • Value
  • ) />

This can be a slow method. You rarely ever want to convert an array to a list. You also now have to worry about list delimiters within the array values. This may or may not be a concern. If you don't want to go the list route, the other method would be to create a user defined function (UDF) that loops through the array comparing values:

  • <cffunction
  • name="ArrayFind"
  • access="public"
  • returntype="numeric"
  • output="false"
  • hint="Finds a value in an array and returns the index. Returns zero on no find.">
  •  
  • <!--- Define arguments. --->
  • <cfargument name="Array" type="array" required="true" />
  • <cfargument name="Value" type="string" required="true" />
  •  
  • <cfscript>
  •  
  • // Define the local scope.
  • var LOCAL = StructNew();
  •  
  • // Loop over array to find value.
  • for (
  • LOCAL.Index = 1 ;
  • LOCAL.Index LTE ArrayLen( ARGUMENTS.Array ) ;
  • LOCAL.Index = (LOCAL.Index + 1)
  • ){
  •  
  • // Check to see if this is the value.
  • if (NOT Compare(
  • ARGUMENTS.Array[ LOCAL.Index ],
  • ARGUMENTS.Value
  • )){
  •  
  • // This value matches, return index.
  • return( LOCAL.Index );
  • }
  •  
  • }
  •  
  • // We did not find a match. Return zero.
  • return( 0 );
  •  
  • </cfscript>
  • </cffunction>

This can be very fast and efficient, but it requires more code.

However, as I mentioned before, because ColdFusion arrays are really the Java object coldfusion.runtime.Array, we have access to a host of Java methods directly accessible from the ColdFusion array object instance. Of particular interest are the methods:

  • Contains( java.lang.Object ) :: boolean
  • ContainsAll( java.util.Collection ) :: boolean
  • IndexOf( java.lang.Object ) :: int
  • LastIndexOf( java.lang.Object ) :: int

Now, remember, since these are built into the ColdFusion objects, we can call them directly. Let's set up some test code:

  • <!--- Set up an array of girls. --->
  • <cfset arrGirls = ListToArray(
  • "Megan,Kim,Laura,Heather,Kit,Courtney,Kim,Stacey"
  • ) />
  •  
  • <!--- Create a collection of girls. --->
  • <cfset colGirls = CreateObject( "java", "java.util.ArrayList" ).Init() />
  •  
  • <!--- Add girls to collection. --->
  • <cfset colGirls.Add( JavaCast( "string", "Kim" ) ) />
  • <cfset colGirls.Add( JavaCast( "string", "Kit" ) ) />

Here we set up a one-dimensional array as well as a collection object (java.util.ArrayList). We have set up the collection to test some of the functionality a little farther down on the page.

Contains( java.lang.Object ) :: boolean

This takes an object and returns true if the object exists in the array (false if not). The caveat here is that if you have an array of numeric values, Java sees them as strings (this will be discussed later on). Therefore, when searching for values, you must explicitly send them as strings.

  • <!--- See if value exists. --->
  • <cfset blnExists = arrGirls.Contains(
  • JavaCast( "string", "Laura" )
  • ) />

This will return true because Laura is in the array. Notice that I am explicitly casting to string for Java use. This is GOOD practice as ColdFusion is not always sure what to do when it comes to type conversions.

ContainsAll( java.util.Collection ) :: boolean

This takes a collection object and determines if every one of the items exists in the array.

  • <!--- See if all values exist. --->
  • <cfset blnExists = arrGirls.ContainsAll(
  • colGirls
  • ) />

Notice that we are sending the collection (java.util.ArrayList) that we created above. This will return true as both Kit and Kim exist in the original array.

IndexOf( java.lang.Object ) :: int

This takes an object and returns the index of the matching value. Be careful; Java is based at Zero. It is not based at One like ColdFusion. Therefore, non-matching results return a -1, NOT zero.

  • <!--- Get index of girl. --->
  • <cfset intIndex = arrGirls.IndexOf(
  • JavaCast( "string", "Heather" )
  • ) />

Notice again that I am always explicitly casting the string. This will return 3 as Heather is the fourth value in the list. In Java, the fourth value, indexed starting at zero, will return 3.

  • <!--- Get index of girl. --->
  • <cfset intIndex = arrGirls.IndexOf(
  • JavaCast( "string", "Bobby Jo" )
  • ) />

This will return -1 as Bobby Jo is NOT in the original list.

LastIndexOf( java.lang.Object ) :: int

This will get the last index of the given value in the array.

  • <!--- Get first index. --->
  • <cfset intFirstIndex = arrGirls.IndexOf(
  • JavaCast( "string", "Kim" )
  • ) />
  •  
  • <!--- Get last index. --->
  • <cfset intLastIndex = arrGirls.LastIndexOf(
  • JavaCast( "string", "Kim" )
  • ) />

This will return 1 and 6 respectively.

How easy is that? All this time (since ColdFusion MX) there have been UDF's written for the very purpose of mimicking something that was already built into the language, albeit hidden from the surface through the ColdFusion facade.

Now, I have only tested this using simple values. I am not sure how this would work using complex objects such as structs. I am guessing that structs of simple values would work fine but no promises. After all, the IndexOf() and Contains() methods do NOT take strings. They take OBJECTS. That is for flexibility.

Just to cover the string vs. number caveat. Assuming we have the following array:

  • <!--- Set up numeric array. --->
  • <cfset arrNums = ArrayNew( 1 ) />
  • <cfset ArrayAppend( arrNums, 50 ) />
  • <cfset ArrayAppend( arrNums, 60 ) />
  • <cfset ArrayAppend( arrNums, 70 ) />
  • <cfset ArrayAppend( arrNums, 80 ) />
  • <cfset ArrayAppend( arrNums, 90 ) />

... the following will NOT find a value:

  • <!--- Find numeric value. --->
  • <cfset blnExists = arrNums.Contains(
  • JavaCast( "int", 70 )
  • ) />

... since it is asking for a numeric match. However, the following WILL find a value:

  • <!--- Find numeric value. --->
  • <cfset blnExists = arrNums.Contains(
  • JavaCast( "string", 70 )
  • ) />

This is because the original values of the array are stored as strings, even though we told them to be numbers. All simple values are stored as strings. You can overcome this by forcing the initial array to be set using JavaCast() and numeric values:

  • <!--- Create an array and force to store numbers. --->
  • <cfset arrNums = ArrayNew( 1 ) />
  • <cfset ArrayAppend( arrNums, JavaCast( "int", 50 ) ) />
  • <cfset ArrayAppend( arrNums, JavaCast( "int", 60 ) ) />
  • <cfset ArrayAppend( arrNums, JavaCast( "int", 70 ) ) />
  • <cfset ArrayAppend( arrNums, JavaCast( "int", 80 ) ) />
  • <cfset ArrayAppend( arrNums, JavaCast( "int", 90 ) ) />

Once you have this, you can search for numeric values. In fact, you have to search for numeric values. Searching for strings at this point will not yield any matches as they did above.

So there you have it. Go explore the wonderful marriage of ColdFusion and Java. This intended for mature audiences only as it might get hot and steamy in that bed ;)

If you want to see what is available in the coldfusion.runtime.Array object, here are the class methods and constructors complete with parameter types and return types:


 
 
 

 
ColdFusion Array Dump - coldfusion.runtime.Array  
 
 
 

Looking For A New Job?

100% of job board revenue is donated to Kiva. Loans that change livesFind out more »

Reader Comments

Good post! Its worth noting that because you're using java methods any string searches are case sensitive. So indexOf("Kim") returns 1 but indexOf("KIM") returns -1.

Another great trick is using the underlying java class to supplement ColdFusion's list functions. One list function I've always wanted to see in CF is the ability to grab a portion of a list. You can use the subList() method to do just that.

<!--- return first two elements of arrNums --->
<cfset subList = arrayToList(arrNums.subList(0, 2))>
<cfoutput>#subList#</cfoutput>

Just remember the indexes are zero based and the fromIndex is inclusive while the end index is exclusive.
http://java.sun.com/j2se/1.5.0/docs/api/java/util/List.html

Reply to this Comment

jM,

Thanks for the link and the solid tip. There are definitely times when it would be nice to get a good sub-list. Sweet.

Reply to this Comment

The subList method, per your list of methods above, returns a java.util.List, which is not the same as coldfusion.runtime.Array. CF docs state that java.util.Lists are converted to comma-delimited lists. However, if you try to use a CF list method, you may get an error: Complex object types cannot be converted to simple values. Also, in CF 8 within cfscript, if you try to access or set an index value on an object returned by subList() by using array notation: arMyArray[i], you are likely to encounter an error: You have attempted to dereference a scalar variable of type class java.util.Collections$SynchronizedRandomAccessList as a structure with members. However, you can set an index value with CF array method arraySet(subList, i, i , newValue), and output via arrayToList. I wonder if conversion is not as complete as it should be?

Reply to this Comment

@Cary,

Yeah, you have to be careful when using Java methods. Usually, when I look at a Java method, it is more out of curiosity. At the end of the day, I default to build in ColdFusion methods... OR, quite powerfully, creating Java objects to use via CreateObject(). (ex. java.util.regex.Pattern).

Reply to this Comment

Post A Comment

You — Get Out Of My Dreams, Get Into My Comments
Live in the Now
Oops!
Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.