Struct Iteration With CFLoop Exposes Both Key And Value In Lucee CFML 5.3.6.61
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.
Want to use code from this post? Check out the license.
Reader Comments
it's a bit quirky... you can use item or index for the key, unless both are defined
https://trycf.com/gist/6774d6a30280941505546f4c930743ad/lucee5?theme=monokai
I have updated the Lucee docs to document this
https://github.com/lucee/lucee-docs/pull/879
https://docs.lucee.org/reference/tags/loop.html#attribute-index
@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.@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 usekey
andvalue
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
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 :)
@All,
So, I was just reading through the Lucee CFML docs earlier this week (as one does), and I realized that
CFLoop
is even cooler than I thought it was. I don't know when this was introduced, but apparently you can even usekey
,value
, andstruct
aliases during Struct-iteration:www.bennadel.com/blog/3916-struct-iteration-with-cfloop-includes-super-intuitive-aliases-in-lucee-cfml-5-3-6-61.htm
This language, man -- just keeps getting more awesome!