Interesting arraySet Behavior

This post is more than 2 years old.

A reader pinged me last night with something he was convinced was a bug, but had said that Adobe shot him down and told him it was expected behavior. The behavior in question involved arraySet, a function I rarely use myself, but is useful for folks who want to quickly initialize multiple values in an array.

What he noticed was that when he used arraySet with structs, each array element pointed to the same structure. Here is an example:

<cfset a = []>

<cfset arraySet(a,1, 5, {})> <cfset a[1].name = "ray">

<cfdump var="#a#">

The result of this is:

As you can see, I only modified the first array element, but all five were modified. We get this result because the structure created within the arraySet function is copied by to all the array elements, but copied by reference. This is expected (although it trips people up) for structures and other complex data. It is also expected, or should be, that if you use a function for the value in arraySet that it will only be run once. So for example:

<cfset arraySet(a,1, 5, createUUID())> <cfdump var="#a#">

When run this returns:

Again, I don't think any of this is wrong, or even unexpected, but I certainly can see forgetting this myself.

Raymond Camden's Picture

About Raymond Camden

Raymond is a senior developer evangelist for Adobe. He focuses on document services, JavaScript, and enterprise cat demos. If you like this article, please consider visiting my Amazon Wishlist or donating via PayPal to show your support. You can even buy me a coffee!

Lafayette, LA https://www.raymondcamden.com

Archived Comments

Comment 1 by Chris Blackwell posted on 4/28/2010 at 3:53 PM

There's a similar gotcha with udf's & loops too, as this example shows - http://pastebin.com/WbYMRUwM

Comment 2 by Raymond Camden posted on 4/28/2010 at 3:55 PM

(FYI, for future reference, I think a code sample like that is short enough to be in the comment. Up to you though. :)

You could fix that by changing:

#event.linkto("some.event", {id=q.id})#

to

#event.linkto("some.event", {id=q.id[currentRow]})#

but I can see how one would expect the original version to work.

Comment 3 by David Hammond posted on 4/28/2010 at 5:41 PM

@Chris, that is a curious bug. At least it seems like a bug to me. It must have something to do with how CF deals with structure literals withing functions -- that's something that was added in CF9. My first thought was, you should be able to do this:

#event.linkto("some.event", {id=id})#

But that produces an error "Variable ID is undefined". So I thought, wait a minute, if that produces an error, can you really do what Ray says and use currentrow? In fact you can't. In my test I got "Variable CURRENTROW is undefined".

Comment 4 by Raymond Camden posted on 4/28/2010 at 5:44 PM

What?? I call BS. :) But I didn't run it. Doing so now.

Comment 5 by David Hammond posted on 4/28/2010 at 5:47 PM

:-) I would have called BS too. Here is my test code:

<cfoutput query="q">
#test({id=q.id[currentrow]})#
</cfoutput>
<cffunction name=test output="true">
<cfargument name="s" type="struct">
#s.id#
</cffunction>

Comment 6 by Raymond Camden posted on 4/28/2010 at 5:51 PM

Wow, I stand corrected! Given this code:

<cfquery name="art" datasource="cfartgallery">
select artid, artname
from art
</cfquery>

<cfscript>
function foo(struct s) {
return serializeJSON(s);
}
</cfscript>

<cfdump var="#art#" top=3>
<cfoutput query="art">
<cfset x = artid[currentrow]>
<!---calling foo with #artid# = #foo({id=art.artid[currentrow]})#<br/>--->
<!--- calling foo with #artid# = #foo({id=x})#<br/> --->
<cfset y = {id=artid}>
calling foo with #artid# = #foo(y)#<br/>
</cfoutput>

The two items commented out both failed. I'm going to file bug reports on this guys - and I apologize for assuming.

Comment 7 by Raymond Camden posted on 4/28/2010 at 5:58 PM

Bug filed.

Comment 8 by David Hammond posted on 4/28/2010 at 6:06 PM

Thanks, Ray. This is disappointing because I was really looking forward to using this kind of code freely (I still have to target CF8 for my current projects.)

One additional note: this doesn't produce an error:
test({id=q.id[q.currentrow]})
but it has the same problem as test({id=q.id}). I guess the structure is being created outside of the cfoutput scope.