Appending One Array To Another With ArrayAppendAll() In ColdFusion
This is just a super quick post about merging or concatenating two arrays in ColdFusion. Currently, the only way to natively join two arrays in ColdFusion is to use arrayAppend(), pushing one value at a time from the incoming array onto the target array. You can use the undocumented Collection method - array.addAll() - which I've done a number of times; but, if you want to keep things completely ColdFusion-friendly, you have to use a ColdFusion user defined function like arrayAppendAll().
To see this UDF in action, we'll create two arrays and then append one to the other:
<cffunction
name="arrayAppendAll"
access="public"
returntype="array"
output="false"
hint="I append (concat) the entire incoming array to the target array.">
<!--- Define arguments. --->
<cfargument
name="targetArray"
type="array"
required="true"
hint="I am the array being augmented"
/>
<cfargument
name="incomingArray"
type="array"
required="true"
hint="I am the array being added to the target array."
/>
<!--- Define the local scope. --->
<cfset var local = {} />
<!---
Loop over the incoming array and add each value to the
target array. If we wanted to get *naughty*, we could use
the Collection-based interface of the array:
array.addAll( array )
However, to keep things ColdFusion-friendly, we'll perform
the array augmentation manually.
--->
<cfloop
index="local.incomingValue"
array="#arguments.incomingArray#">
<!--- Append the incoming value. --->
<cfset arrayAppend(
arguments.targetArray,
local.incomingValue
) />
</cfloop>
<!---
Since arrays are passed by VALUE in ColdFusion, we have to
return the resultant array.
--->
<cfreturn arguments.targetArray />
</cffunction>
<!--- ----------------------------------------------------- --->
<!--- ----------------------------------------------------- --->
<!--- ----------------------------------------------------- --->
<!--- ----------------------------------------------------- --->
<!--- Create an array of women. --->
<cfset women = [ "Sarah", "Tricia", "Joanna" ] />
<!---
Create an incoming array of women that we want to add to
the first one.
--->
<cfset incomingWomen = [ "Katie", "Jill", "Erica" ] />
<!---
Add one array to the other.
NOTE: Since ColdFusion arrays are passed by VALUE, we have to
overwrite the "women" variable with the resultant array.
--->
<cfset women = arrayAppendAll( women, incomingWomen ) />
<!--- Output the list of women. --->
<cfoutput>
Women: #arrayToList( women, " - " )#
</cfoutput>
As you can see, we're still looping over the incoming array, copying over its values one index at a time; but, since this is now encapsulated in a UDF, it's much easier to do. Running the above code gives us the following output:
Women: Sarah - Tricia - Joanna - Katie - Jill - Erica
As you can see, we were able to concatenate the two arrays.
Since arrays are passed by value in ColdFusion, our UDF cannot work directly on the original array. Instead, arrayAppendAll() has to work on its own copy which it then returns as a result of the function execution. It is then up to the calling context to store that resultant array back into the most appropriate variable.
I'd really like an arrayAppendAll() function (or something similar) to be added to the ColdFusion core language. I find this kind of situation comes up a lot. And, since it's a method of the underlying Collection interface, I have to believe that it would be really easy for the ColdFusion engineers to implement.
Want to use code from this post? Check out the license.
Reader Comments
Au contraire sir, if you simply write your udf to accept strings instead of variables you'll be able to inject the resulting array into the calling variables scope and get the result you're looking for.
The java way is simpler, and faster.
@David,
That would only work IF you knew the name of the variable referencing the array that is being updated. I suppose you could pass in the "variable path" to the array being augmented.
@Mark,
Agreed; I feel like Adobe has never came out and took a stand on whether or not we *should* use the underlying Java methods. I use them personally, but I'd love to hear a definitive answer.
A while back, I realized that, to some degree, you *have* to use the Java methods on CF objects simply because ColdFusion doesn't allow you to differentiate the two types:
www.bennadel.com/blog/1023-ColdFusion-Wants-You-To-Access-The-Underlying-Java-Methods.htm
I also tested this on Vectors as well. Create an instance of a Java Vector and isArray( javaVector ) will return "True".
Since we can't natively differentiate between ColdFusion data structures and Java data structures with "similar" interfaces, it would lead us to the conclusion that ColdFusion wants us to use the underlying Java methods.
You know Ben, CF underlying Java core works better for this. ColdFusion arrays are actually an implementation of java list (java.util.List). So all the java list methods can easily be used with CF. So to merge two arrays (which is essentially what you're doing) use the list.addAll() method.
For instance, you have two arrays:
<cfset women = [ "Sarah", "Tricia", "Joanna" ] />
<cfset incomingWomen = [ "Katie", "Jill", "Erica" ] />
Then all you have to do to merge the arrays is
<cfset women.addAll(incomingWomen ) />
Much simpler and cleaner.
I wish I could take credit for it but its based on a blog entry by Rupesh Kumar of Adobe.
http://coldfused.blogspot.com/2007/01/extend-cf-native-objects-harnessing.html
regards,
larry
@Larry,
Yeah, that .addAll() method is cool (as I mentioned in my first paragraph :P). That said, thanks for the link to Rupesh's blog. Since he's an Adobe ColdFusion engineer, this looks like a pretty official position that we *can* use the Java methods. Rockin :)
Why not use the Java?
1) Well, other coldfusion implementations may not support it (Railo, OpenBD, BD.NET). If you want to be portable across CF runtimes, you're pretty much stuck with plain jane cfml.
2) Adobe might change their API in a future release.Thing is, they could do the same with the CFML. :) That's less likely, but unless they drastically change implementations, I don't see it as a problem.
I've done this with the lists and stuff for years, since the list performance on cf7 and earlier was terrible. cf8 and 9 both improved the situation greatly, so I don't rely on it for list stuff as much anymore.
In fact, I even routinely use undocumented java functions. Typically from within a java library, fronted by a cfc or another facade so other devs don't really have to touch the java.
For example, I use the java jackson library in conjunction with a coldfusion.sql.QueryTable to generate non-messed up json (with the same performance as cf8's native json) and I recently implemented ehcache integration for cf8.
It's been so many years since CF went java, and so many devs are still afraid of it.
Hi Ben,
We can also handle the Array append with the help of ArrayToList() and ListToArray() in easy way.
I've got several versions of CF to deal with so compatibility is paramount for me. This works great, thanks!
@Mark,
Yeah, the Java methods are cool. I've often used them when dipping down into String's replaceFirst() and replaceAll() methods. There's always a chance that something will change; but from what I've read, it seems to be generally held as safe to use these kind of approaches.
@Manoj,
You can definitely do that, so long as all of your array contents are simple values. If you had an array of structs, for example, ColdFusion wouldn't be so happy :)
@Frank,
Good stuff.