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

Posted November 24, 2009 at 9:23 AM

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.

 Launch code in new window » Download code as text file »

  • <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):

 Launch code in new window » Download code as text file »

  • <!--- 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.

Download Code Snippet ZIP File

Post Comment  |  Ask Ben  |  Other Searches  |  Print Page




Learning ColdFusion 9 - ColdFusion 9 tutorials, samples, examples, demos

Reader Comments

Nov 24, 2009 at 10:25 AM // reply »
10 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 »
7,207 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 »
74 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 »
7,207 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).


Post Comment  |  Ask Ben

Recent Blog Comments
Feb 9, 2010 at 4:14 AM
Ask Ben: Converting a Query to an Array
Exellent script! Tried to make it shorter, gained approx 10% performance improvement :) [CODE] <cffunction name="queryToArray" access="public" returntype="array" output="false"hint="This turns a q ... read »
Feb 9, 2010 at 3:16 AM
Using jQuery's SlideUp() and SlideDown() Methods With Bottom-Positioned Elements
Just a quick fix for the code example above: $('.panel').height() should instead be... $('.panel').outerHeight() ... read »
Feb 9, 2010 at 3:09 AM
Using jQuery's SlideUp() and SlideDown() Methods With Bottom-Positioned Elements
I came across this page because I was searching for a certain behavior. I didn't just want the element to slide up; I wanted it's contents to slide up with it. As if the contents were written on a p ... read »
Feb 9, 2010 at 12:57 AM
Ask Ben: Creating A PDF And Attaching It To An Email Using ColdFusion
Have you got any reference to code explaining the procedure to use cfdocument, file to disk and attach to email? ... read »
Feb 8, 2010 at 11:27 PM
Why NULL Values Should Not Be Used in a Database Unless Required
@Randi, While I can appreciate your specific situation, I personally am not a fan of a situation where people have the option to run their own ad-hoc reports on the database. This is, honestly, a r ... read »
Feb 8, 2010 at 11:15 PM
Creating Microsoft Excel Documents With ColdFusion And XML
@Candice, No problem - glad you got it figured out. @David, To view the XLS document as an XML file, if I remember correctly, I actually opened up the document in Excel and then went "File > ... read »
Feb 8, 2010 at 11:11 PM
Converting An IP Address To An Integer Using MySQL (Thanks Julian Halliwell)
@Rob, As @Julian pointed out, you need to enable multiple queries in order to run queries containing semi-colons. For more info on that, take a look at this post: http://www.bennadel.com/blog/120 ... read »
Feb 8, 2010 at 11:08 PM
Muscle: Confessions Of An Unlikely Bodybuilder By Samuel Wilson Fussell
@Robert, Awesome. When you start reading it, I'd love for you to drop by and share your comments. I happen to love the book and am always happy to have a good conversation about it. ... read »