Skip to main content
Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.

Struct Iteration With CFLoop Exposes Both Key And Value In Lucee CFML 5.3.6.61

By Ben Nadel on
Tags: ColdFusion

Back in February, when I was having lunch with Gert Franz, co-creator of Lucee CFML, we were commiserating on how amazing ColdFusion is. At that lunch, Gert told me about some of the great things that Lucee CFML offers, like its seamless support for ColdFusion Tags in CFScript. Another minor feature that he mentioned was that the CFLoop tag exposes both Key and Value attribute that remove the need to look-up the value within the CFLoop body. Now, months later, I finally used this feature for the first time in yesterday's post on proxying Amazon S3 uploads using CFHTTP and Lucee CFML. Given the fact that Adobe ColdFusion doesn't support this feature, I thought it might be worth exploring in Lucee CFML 5.3.6.61 in case new Lucee-converts didn't realize it was there (just as I didn't).

To paint this historical picture of what looping over a Struct in Adobe ColdFusion would look like, I am sure this type of code is near-and-dear to many developer's hearts:

<cfset data = {
	hello: "world",
	foo: "bar",
	missing: javaCast( "null", "" ),
	cool: "beans"
} />

<!---
	When looping over a Struct with the CFLoop tag in Adobe ColdFusion, we only have the
	Item attribute, which gives us the key in the iteration. Once inside the iteration,
	we have to look up the Value for ourselves (assuming the key exists).
--->
<cfloop collection="#data#" item="key">

	<cfif structKeyExists( data, key )>

		<!--- We have manually extract the value from the Struct. --->
		<cfset value = data[ key ] />
		<cfset writeOutput( key & " : " & value & "<br />" ) />

	<cfelse>

		<cfset writeOutput( key & " : [undefined] <br />" ) />

	</cfif>

</cfloop>

As you can see, within the CFLoop tag, all we have access to on the Struct is the item, which exposes the iteration key. With that key, we then have to manually extract the value from the Struct as we perform the iteration.

With the CFLoop tag in Lucee CFML, we can leverage both the item and the index attributes in order to access both the value and the key, respectively:

<cfscript>

	data = [
		hello: "world",
		foo: "bar",
		missing: nullValue(),
		cool: "beans"
	];

	// In Lucee CFML (not currently supported in Adobe ColdFusion), when you iterate over
	// a Struct using the CFLoop tag, you can provide BOTH an INDEX and an ITEM attribute
	// which grant you access to both the Key (index) and the Value (item).
	loop
		collection = data
		index = "key"
		item = "value"
		{

		echo( key & " : " & ( value ?: "[undefined]" ) & "<br />" );

	}

	echo( "<br />" );

	// Of course, if you were to use Function-based iteration, you can also access both
	// the Key and the Value as arguments on the iteration operator.
	data.each(
		( key2, value2 ) => {

			echo( key2 & " : " & ( value2 ?: "[undefined]" ) & "<br />" );

		}
	);

</cfscript>

As you can see, in our CFLoop tag, we're mapping the index to the key and the item to the value. And, when we run this code, we get the following browser output:

hello : world
foo : bar
missing : [undefined]
cool : beans

hello : world
foo : bar
missing : [undefined]
cool : beans

Awesome! As you can see, we get declarative access to both the key and value during Struct iteration - no need to imperatively access any data.

And, for funzies, I also threw in the .each() approach as well in order to demonstrate that function-based traversal also exposes both the key and the value.

This may seem like a tiny little feature; and, it totally is. But, part of what makes Lucee CFML such a joy to work with is that is jam-packed with tiny little features like this one.


Reader Comments

@Zac,

I am just amazed how fast you all update stuff. I feel so fortunate to be part of such a proactive community. The behavior is a little funny with the optional argument. The truth is, in 99.99% of cases, I iterate over Structs using a simple for-in loop. And the reality is, I almost never need to iterate over Structs. But, once in a blue-moon I due, and it's nice to know this is a feature I can leverage.

Reply to this Comment

@All,

In this post, you may notice that the second example with .each() is using differently-named arguments. This is for a reason - if I were to use key and value in the .each() version, I would have gotten an "unexpected" outcome because of the scope cascade in ColdFusion. Since this is a fun little behavior, I thought I would call it out specifically:

www.bennadel.com/blog/3852-scope-traversal-behavior-with-undefined-function-arguments-in-lucee-cfml-5-3-6-61.htm

Reply to this Comment

Thanks Ben, back at ya!

As senior devs, it's always fun to fix / improve things as we have the knowledge to do it, hopefully making the lives of other cfml developers easier!

I've been working on and with Lucee for a years now, it's great that you're also involved and sharing your love for cfml with Lucee via your blog posts!

Thanks for the fresh inspiration to improve things with each post :)

Reply to this Comment

Post A Comment

You — Get Out Of My Dreams, Get Into My Blog
Live in the Now
Oops!
NEW: Some basic markdown formatting is now supported: bold, italic, blockquotes, lists, fenced code-blocks. Read more about markdown syntax »
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.