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

Posted September 6, 2006 at 3:42 PM by Ben Nadel

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  
 
 
 


Reader Comments

jM
Nov 19, 2006 at 2:01 AM // reply »
9 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


Nov 20, 2006 at 7:49 AM // reply »
11,238 Comments

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.


Dec 15, 2008 at 11:13 AM // reply »
14 Comments

Freakin' awesome! Didn't want to do a bunch of ListFinding with 10,000 entries, so this method will be much better. Thx.


Jul 8, 2009 at 5:50 PM // reply »
1 Comments

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?


Jul 10, 2009 at 4:03 PM // reply »
11,238 Comments

@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).


Post A Comment

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.

Please review the following issues:

Author Name:


Author Email:

Author Website:

Comment:

Supported HTML tags for formatting: <strong>bold</strong>   <em>italic</em>   <code>code</code>







  • Help Wanted - Find Your Next ColdFusion Job
Ben Nadel's Company - Epicenter Consulting Recent Blog Comments
May 17, 2013 at 7:42 PM
HashKeyCopier - An AngularJS Utility Class For Merging Cached And Live Data
Ben - thanks so much for posting these Angular articles and findings, they've been a huge help towards learning one of the more 'complex' JavaScript frameworks out there (IMO). I have been using Angu ... read »
May 16, 2013 at 5:01 PM
UPDATE: Parsing CSV Data Files In ColdFusion With csvToArray()
Your code was the closest thing I've found to obtaining some direction for converting ISO fields to values that CF can translate properly. Thank you for posting! ... read »
May 15, 2013 at 10:37 PM
Very Simple Pusher And ColdFusion Powered Chat
hi id making plz easy ... read »
May 15, 2013 at 6:07 PM
Making SOAP Web Service Requests With ColdFusion And CFHTTP
Ben, you once again saved my bacon at work. Thank you, thank you, thank you! ... read »
May 15, 2013 at 4:15 PM
What If All User Interface (UI) Data Came In Reports?
@Josh, Thanks! @Ben, I definitely recommend the David West book "Object Thinking" I've been quoting from. It goes deeply into the philosophy and history of OO programming. His breadth ... read »
May 15, 2013 at 11:36 AM
Ask Ben: Print Part Of A Web Page With jQuery
I found this helpfull when you need to keep (refresh) the original parent page after closing the iframe child print dialog (Hoping you're not using a form at this time so it won't submit again): On ... read »
May 14, 2013 at 7:13 PM
What If All User Interface (UI) Data Came In Reports?
@Jonah, If there's any books you'd recommend on the subject of domain modelling, I'd love to hear it. I just downloaded the free PDF of "Domain Driven Design Quickly". Figured I'd give it ... read »
May 14, 2013 at 6:57 PM
The UX Of Prototyping: Low-Fidelity Is The New High-Fidelity
@Phillip, I'm not sure I follow what you mean? Are you saying that you looked at the list of widgets provided by the jQuery UI and let that be your style guide? ... read »
InVision App - Prototyping Made Beautiful With Prototyping Tools