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 BFusion / BFLEX 2009 (Bloomington, Indiana) with:

Exploring Array FoldLeft() Concepts In ColdFusion

Posted by Ben Nadel
Tags: ColdFusion

As I've been making my way through the Seven Languages in Seven Weeks book by Bruce Tate, one of the language features that I find myself consistently enjoying is that of "fold left." Folding, in general, is the act of applying an operator and a running product to each value within an array in order to produce some final product that has been aggregated across the array. Fold "left" simply means that the we run from 1 to N. Fold "right," on the other hand, simply means that we run from N to 1. In my studies, it appears to be a super useful feature, so I wanted to try and implement it in ColdFusion.

Before we look at any code, it should be noted that I am running this demo in the context of my CFML preprocessor that makes inline function definition possible in ColdFusion. The preprocessor is using nothing more than variable substitution; as such, the same thing could be achieved with predefined functions.

That said, let's take a look at the function signature of arrayFoldLeft():

arrayFoldLeft( array, initialValue, operator ) :: product

The array parameter is the array over which we are applying the operator. The initialValue parameter is the "running product" that gets passed to the first array index (ie. the first execution of the operator). The operator parameter is a binary operator - a function that takes two arguments: the running product and the value located at the current array index:

operator( currentProduct, currentValue ) :: runningProduct

The operator will be applied at every index of the array and the return value of the operator execution will be passed-in as the "running product" to the next operator execution.

Ok, let's take a look at a demo of the arrayFoldLeft() function. And remember, this was run in the context of my CFML preprocessor:

  • <cffunction
  • name="arrayFoldLeft"
  • access="public"
  • returntype="any"
  • output="false"
  • hint="I fold a product across the length of an array from 1 to N.">
  •  
  • <!--- Define arguments. --->
  • <cfargument
  • name="array"
  • type="array"
  • required="true"
  • hint="I am the array over which we are performing the foldLeft action."
  • />
  •  
  • <cfargument
  • name="initialProduct"
  • type="any"
  • required="false"
  • hint="I am the initial value that gets passed as the product to the first array index."
  • />
  •  
  • <cfargument
  • name="operator"
  • type="any"
  • required="true"
  • hint="I am the binary operator that will be applied to each array value. I take the current product and the current value."
  • />
  •  
  • <!--- Define the local scope. --->
  • <cfset var local = {} />
  •  
  • <!---
  • Start out with our product if the initial product is
  • available. If not, then we'll just leave the local
  • product undefined.
  • --->
  • <cfif !isNull( arguments.initialProduct )>
  •  
  • <!--- Set the initial local product. --->
  • <cfset local.product = arguments.initialProduct />
  •  
  • </cfif>
  •  
  • <!---
  • Loop over each array index and apply the given operator
  • to the value at the current index.
  • --->
  • <cfloop
  • index="local.value"
  • array="#arguments.array#">
  •  
  • <!---
  • Check to see if the product is currently available. If
  • not, we need to explicitly pass NULL into the operator
  • since we can't refer to a null value directly.
  • --->
  • <cfif isNull( local.product )>
  •  
  • <!--- Padd in a null product. --->
  • <cfset local.product = arguments.operator(
  • javaCast( "null", "" ),
  • local.value
  • ) />
  •  
  • <cfelse>
  •  
  • <!--- Pass in the current product. --->
  • <cfset local.product = arguments.operator(
  • local.product,
  • local.value
  • ) />
  •  
  • </cfif>
  •  
  • </cfloop>
  •  
  • <!---
  • Return the aggregated product. Check to see if it is null.
  • If it is, we need to return Void.
  • --->
  • <cfif isNull( local.product )>
  •  
  • <!--- Return void. --->
  • <cfreturn />
  •  
  • <cfelse>
  •  
  • <!--- Return the product. --->
  • <cfreturn local.product />
  •  
  • </cfif>
  • </cffunction>
  •  
  •  
  •  
  • <!--- ----------------------------------------------------- --->
  • <!--- ----------------------------------------------------- --->
  • <!--- ----------------------------------------------------- --->
  • <!--- ----------------------------------------------------- --->
  •  
  •  
  •  
  • <!--- Build up an array of numbers. --->
  • <cfset numbers = [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ] />
  •  
  • <!---
  • Now, get the multiplication product of all the numbers in the
  • array (ie. what is the value of all the numbers in the array
  • being multiplied together).
  • --->
  • <cfset product = arrayFoldLeft(
  • numbers,
  • 1,
  • <function( currentProduct, value ){
  • <!--- Multiple the current product by the given value. --->
  • <cfreturn( currentProduct * value ) />
  • }>
  • ) />
  •  
  •  
  • <!--- Output the product. --->
  • <cfoutput>
  •  
  • Product of Multiplication: #product#
  •  
  • </cfoutput>

In this demo, we are taking an array of numbers and figuring out what the product of all the numbers is. arrayFoldLeft() makes this concept quite simple; by folding the current product across the array, it makes it easy to multiply each number by the product of all the numbers before it. And, when we run the above code, we get the following output:

Product of Multiplication: 362880

When we call arrayFoldLeft(), we are passing in "1" as our initial value because anything times 1 is itself. Once we get past the first index, our running product then becomes the result of the previous operator.

This might seem like a lot of code because I had to define the actual arrayFoldLeft() method; but, if that was core to ColdFusion, running operations across an array would be as easy as: array, initial value, and operator! Of course, part of what makes this approach so appealing (to me) is the use of inline functions to make the entire operation all that much more succinct.




Reader Comments

@Lola,

In the homework, the most practical exercise that I saw was applying a collection of data to a given value. So, for example, let's say you have a block of text and you want to programmatically link given words in that block of text to a external pages (like linking dictionary words). You could fold the block of text across the collection of words, adding appropriate links as need-be.

Remember, the core functionality here is nothing new. We could do the same exact thing with a CFLoop/Array in which we manually kept a running product of the per-index operations. The only thing that foldLeft() adds for a CFLoop/Array is that it shoulders some of the lifting behind the scenes.

In the end, foldLeft() is just a convenience, not a really new type of functionality.

Reply to this Comment

@Randall,

Like I told Sean in one of my posts, I am sure I would quickly abuse foldLeft(). I guess there's something about it that I just personally find very appealing. You can always loop over an array manually, but having the loop logic encapsulated just seems, juicy :)

Reply to this Comment

I hope somebody will post more possible real-world uses for this as it does seem like it could be extremely handy -- but right now I can't see it.

Reply to this Comment

@Randall,

Another use case for this might be to map one collection onto another. Most other languages have the concept of map() or filter() or things to that effect; but, the same could be accomplished with the foldLeft() approach:

  • <cfset numbers = [ 1, 2, 3, 4, 5 ] />
  •  
  • <cfset odds = foldLeft(
  • numbers,
  • [],
  • <function( values, value ){
  • <cfif (value mod 2)>
  • <cfset arrayAppend( values, value ) />
  • </cfif>
  • <cfreturn values />
  • }>
  • ) />

Here, we are mapping the numbers array onto an array that holds only the odd values. The "values" array is being built up over the collection of all numbers.

Might not be the best example, but you can start to see how this might be a very flexible function.

Reply to this Comment

@ Lola

You probably know far more than me, but I too ask your type of questions. "How can I use this stuff? what for?"

I know nothing about Cold Fusion but have Version 9 on my Mac along w/ Dreamweaver found in CS 5. I've started with this link but it's over my head:

http://help.adobe.com/en_US/ColdFusion/9.0/CFMLRef/index.html

I know enough Dreamweaver (DW) to be dangerous. I started with Macromedia DW, then moved up to CS2, now to 5. I'm more of a philosopher / scientist and can only wish to be as smart as some of youz guyz on here when it comes to computer languages.

I'm fluent in 2 languages (human languages), can get by in about 3 others, but just stupid as heck when it comes to computer languages. HTML is a klunky for me. I want to become more fluid at computer codes. I own DW, and everything else contained in the Masters Collection of Adobe CS 5. I sure would like to be able to learn alot of it. Perhaps you can give me a hand. I'm disappointed in my website and know I can do so much more since I own the software.

Thanks!

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.