ColdFusion ArraySplice() Method As An Example Of A Dynamic Method Signature

Posted November 24, 2009 at 9:23 AM by Ben Nadel

Tags: ColdFusion

Last week, I blogged about using ColdFusion's CFParam tag as a possible way to enforce CFArgument-like functionality in a situation where you might not know which arguments will exist at compile time. That post spun off into a very interesting conversation about the pros and cons of using named-arguments versus ordered-arguments and when each makes sense (or should be required).

I think the biggest factor that directed that conversation was the fact that in my example method, optional arguments could precede other optional arguments, depending on the number of arguments being passed-in. In a language like ColdFusion where different method signatures don't necessitate different physical functions, there is definitely a funky feeling to that. As such, I wanted to come up with a better example method in which the optional arguments were always at the end of the arguments list.

While this doesn't relate at all back to the concept of using CFParam to define dynamic arguments, I thought porting over a Splice() method (from Javascript) to ColdFusion, was a great example of a highly dynamic method signature. If you are not familiar with Javascript's Array::Splice() method, it can both delete 0..N elements and insert 0..N elements from and to an array at the same time.

  • <cffunction
  • name="arraySplice"
  • access="public"
  • returntype="array"
  • output="false"
  • hint="I splice an array by deleting and / or adding new elements at the given index.">
  •  
  • <!--- Define arguments. --->
  • <cfargument
  • name="array"
  • type="array"
  • required="true"
  • hint="I am the array being manipulated."
  • />
  •  
  • <cfargument
  • name="index"
  • type="numeric"
  • required="true"
  • hint="I am the index at which to start adding or removing elements to and from the array (respectively)."
  • />
  •  
  • <cfargument
  • name="howMany"
  • type="numeric"
  • required="true"
  • hint="I am the number of elements that should be removed from the array, starting at the given index. If zero, no elements will be removed."
  • />
  •  
  • <!---
  • At this point, the method can take a variable number of
  • subsequent arguments that would be the elements to insert
  • into the array at the given index.
  •  
  • <cfargument name="element1" />
  • <cfargument name="element2" />
  • <cfargument name="element3" />
  • ....
  • <cfargument name="elementN" />
  • --->
  •  
  • <!--- Define the local scope. --->
  • <cfset var local = {} />
  •  
  • <!---
  • The first thing we want to do is delete any elements from
  • the array that we need to. Since ColdFusion doesn't have
  • an inherent way to delete more than one item, we will have
  • to perform this as a conditional loop.
  • --->
  • <cfset local.deleteCounter = arguments.howMany />
  •  
  • <!---
  • Keep deleting while the DeleteCounter is still non-zero
  • AND while the array length is big enough to encompass the
  • given index.
  • --->
  • <cfloop condition="(local.deleteCounter-- && (arrayLen( arguments.array ) gte arguments.index))">
  •  
  • <!--- Delete the given item in the array. --->
  • <cfset arrayDeleteAt(
  • arguments.array,
  • arguments.index
  • ) />
  •  
  • </cfloop>
  •  
  • <!---
  • Now that we have delete any necessary elements from the
  • array, we need to add any additional items, starting at
  • the given instance.
  • --->
  •  
  • <!---
  • Since we will need to keep updating the index of the
  • insert, start a new pointer for that index.
  • --->
  • <cfset local.insertIndex = arguments.index />
  •  
  • <!---
  • Loop over the optional elements in the arguments. There
  • may be ZERO or more of these elements.
  • --->
  • <cfloop
  • index="local.elementIndex"
  • from="4"
  • to="#arrayLen( arguments )#"
  • step="1">
  •  
  • <!---
  • When inserting, we need to make sure that the index
  • of insertion is not beyond the bounds of the array.
  • If it is, we need to do an append (or the insert
  • will error).
  • --->
  • <cfif (local.insertIndex gt arrayLen( arguments.array ))>
  •  
  • <!--- Append the new element. --->
  • <cfset arrayAppend(
  • arguments.array,
  • arguments[ local.elementIndex ]
  • ) />
  •  
  • <cfelse>
  •  
  • <!--- Insert the new element. --->
  • <cfset arrayInsertAt(
  • arguments.array,
  • local.insertIndex,
  • arguments[ local.elementIndex ]
  • ) />
  •  
  • </cfif>
  •  
  • <!--- Increment insertion index. --->
  • <cfset ++local.insertIndex />
  •  
  • </cfloop>
  •  
  • <!--- Return the updated array. --->
  • <cfreturn arguments.array />
  • </cffunction>

As you can see, the first three arguments to the ArraySplice() method are always static; but then, you can append as many additional arguments to the method call as you want for the insert functionality. To see this in action, take a look at the following demo (I have created comments for the intermediary results):

  • <!--- Create an array of test values. --->
  • <cfset values = [ 1, 2, 3 ] />
  •  
  • <!--- Delete the last two values. --->
  • <cfset values = arraySplice( values, 2, 2 ) />
  • <!--- // [ 1 ]. --->
  •  
  • <!--- Insert two values at the end. --->
  • <cfset values = arraySplice( values, 2, 0, "A", "B" ) />
  • <!--- // [ 1, "A", "B" ]. --->
  •  
  • <!--- Insert two values at the beginning. --->
  • <cfset values = arraySplice( values, 1, 0, "Z", "Y" ) />
  • <!--- // [ "Z", "Y", 1, "A", "B" ]. --->
  •  
  • <!---
  • Both delete the middle value and insert two more. Notice
  • that delete and insert use the same index value.
  • --->
  • <cfset values = arraySplice( values, 3, 1, "*", "*" ) />
  • <!--- // [ "Z", "Y", "*", "*", "A", "B" ]. --->
  •  
  •  
  • <!--- Output the array (as a list). --->
  • <cfoutput>
  • #arrayToList( values, ", " )#
  • </cfoutput>

When we run this combination of deletes and inserts, we get the following array-to-list output:

Z, Y, *, *, A, B

Nothing too technically savvy going on here, other than the CFLoop condition that both sets and tests a value (sweeet!); mostly, I thought the ArraySplice() method was just an elegant example of a method signature that could take a variable number of arguments.




Reader Comments

Nov 24, 2009 at 10:25 AM // reply »
11 Comments

Would using the built in java functions be faster on large arrays?

exp:

<cfset var myArray = createObject("java", "java.util.ArrayList").Init(arguments.array) />

...

<cfreturn myArray.subList(JavaCast("int", arguments.index - 1), JavaCast("int", howMany)) />


Nov 24, 2009 at 10:42 AM // reply »
11,238 Comments

@Christopher,

I think using the underlying Java stuff is definitely exciting and often times adds efficiencies. I just worry sometimes about using undocumented stuff; I used to be very pro it, but people have made me wear of it.

Perhaps it's time to rebuild my courage :)


Nov 25, 2009 at 4:51 AM // reply »
149 Comments

Nice to see someone giving CF a little "dynamic method signature" discussion love.

Intelligently overloaded function signatures are one of the most powerful techniques for quality API authoring. jQuery's selector method is probably the greatest single example of how you can consolidate an entire spectrum of implementation and functionality into a single, pristine entry point. A lot of the CFML extension utilities I use behave differently depending on the method signature. One side effect I've noticed is that the more flexible and multifunctional the API functions you author are, ironically, the easier it becomes to remember those variant signatures and what they do. Great post.


Nov 26, 2009 at 9:46 AM // reply »
11,238 Comments

@David,

Glad you like. It is some really cool functionality, but one that I have not had the best chance to use yet (or at least not that I have thought of).


Mar 17, 2010 at 7:50 PM // reply »
1 Comments

Java is awesome and I love seeing new twists on it.

@Ben - I totally agree with you.


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 21, 2013 at 9:25 AM
Turning Off and On Identity Column in SQL Server
you are awesome..i am lucky to get this blog between such a garbage one....Thanks, Prashant ... read »
May 20, 2013 at 4:38 PM
Using A Dynamic Column Name With ValueList() In ColdFusion
@Dana, Your confusion is well founded, since this is a very confusing features. In fact, it ONLY works if you use array notation. Meaning, that this: arrayToList( query[ "columnName" ] ) ... read »
May 20, 2013 at 4:34 PM
Using A Dynamic Column Name With ValueList() In ColdFusion
I was thinking chicken and the egg, I wouldn't have expected it to work in the valuelist going in I guess. Maybe I just need a beer, long day :) ... read »
May 20, 2013 at 4:29 PM
Using A Dynamic Column Name With ValueList() In ColdFusion
@Dana, That's if you're trying to reference a specific row. In this case, we're trying to reference the entire query column as one cohesive value. So, you are correct that if you wanted to output a ... read »
May 20, 2013 at 4:24 PM
Using A Dynamic Column Name With ValueList() In ColdFusion
I thought when you used array notation to reference queries you always had to have the row or it would throw a similar error as well? ... read »
May 20, 2013 at 11:45 AM
Using jQuery's Animate() Step Callback Function To Create Custom Animations
This is really useful. I found out that you don't actually have to use a dummy css property (surprisingly). To animate a property in a linear-gradient for instance I did this this.css('someLinearGra ... read »
May 20, 2013 at 10:51 AM
Using A Dynamic Column Name With ValueList() In ColdFusion
@Josh, Oh snap! You're totally right! I'm not sure I've ever tried that. I did know that you can call a number of other array-methods on ColdFusion query columns: http://www.bennadel.com/blog/167 ... read »
May 20, 2013 at 10:45 AM
Using A Dynamic Column Name With ValueList() In ColdFusion
@Ben - I believe you can achieve the same functionality with ColdFusion's built in ArrayToList() function. ArrayToList( users[ "id" ] ); ... read »
InVision App - Prototyping Made Beautiful With Prototyping Tools