Unfortunately it's too late to strive for consistency at this point. The odd behaviour of structKeyExists is due to the underlying Java implementation, the inconsistent handling of null values in Adobe CFML, and backwards compatibility concerns.
CF structs are special Java objects that implement the java.util.Map interface. The method to retrieve a value in a Map is get(key). Map.get(key) returns a Java null if the key does not exist in the map. However if the key exists but the value is a Java null, then Map.get(key) also returns null, so Map.get(key) alone is insufficient to tell whether the key exists or not. Given the behaviour of CF's structKeyExists(), it is extremely likely that Adobe implemented it via Java's Map.get(key). The issue could be worked around by exposing Java's Map.containsKey(key), but that would expose the other two problems. CF is still a bit confused about null values. In many cases CF treats a variable with a null value as if it doesn't exist (much like a struct key with a null value). At other times it automatically converts it to an empty string. Changing structKeyExists() to return true for keys with null values would also require a sensible way for CF to handle those null values. A number of proposals have been made about this. My preference is the implementation of a proper "void" type (whose only possible value is null) in CFML. That brings us to the third problem. There is lots and lots of legacy code that relies in the existing behaviour that has been around since ColdFusion MX. I've written <cfif structKeyExists(arguments,"foo")> more times than I can count. If structKeyExists() were changed to return true for keys with null values, a lot of that code would break. Adobe's policy for the evolution of CF has been to avoid the creation of such severe backwards-incompatibilities, and I don't see Adobe changing it's position on this any time soon. Someone who feels passionately about it should file a bug report (if they haven't already), but I'm almost certain that Adobe will treat it as a "wontfix". -- Dennis On 6 January 2012 13:59, Dale Fraser <d...@fraser.id.au> wrote: > Well I originally said no because**** > > ** ** > > Accessing it other ways fails**** > > ** ** > > But it’s clearly there, it needs to be consistant**** > > ** ** > > The name of structKeyExists is perhaps wrong, because it really releates > to the data, not the key.**** > > ** ** > > Regards**** > > Dale Fraser**** > > ** ** > > http://dale.fraser.id.au**** > > http://cfmldocs.com**** > > http://learncf.com**** > > http://flexcf.com**** > > ** ** > > *From:* cfaussie@googlegroups.com [mailto:cfaussie@googlegroups.com] *On > Behalf Of *MrBuzzy > *Sent:* Friday, 6 January 2012 1:47 PM > > *To:* cfaussie@googlegroups.com > *Subject:* Re: [cfaussie] Count the number of arguments passed into a > function.**** > > ** ** > > Yeah, so do you agree structkeyexist should return true for that key? **** > > ** ** > > It exists, but it's not defined. **** > > > On 06/01/2012, at 13:25, "Dale Fraser" <d...@fraser.id.au> wrote:**** > > But the key to the struct exists J I can dump it.**** > > **** > > Regards**** > > Dale Fraser**** > > **** > > http://dale.fraser.id.au**** > > http://cfmldocs.com**** > > http://learncf.com**** > > http://flexcf.com**** > > **** > > *From:* cfaussie@googlegroups.com [mailto:cfaussie@googlegroups.com] *On > Behalf Of *MrBuzzy > *Sent:* Friday, 6 January 2012 12:56 AM > *To:* cfaussie@googlegroups.com > *Subject:* Re: [cfaussie] Count the number of arguments passed into a > function.**** > > **** > > Hey Bau & Co, this one's been bending my brain for a few hours now. It's > an interesting thread I read it lots. Please excuse me for just rambling a > bit, so I can sleep tonight. > > @Dale **** > > I agree StructKeyExists will return false when the key exists, but the > value is null, ie;**** > > <cfset theStruct={'theKey'=javacast('null',0)}>**** > > <cfdump var= "#theStruct#">**** > > <cfdump var="#structKeyExists(theStruct,'theKey')#"><!--- outputs NO --->* > *** > > I *think* I agree it's a bug. But with CF that key with a null value is > not very useful. It's one of those things that's existed for a long time > and for good or bad, it's one reason I use IsDefined() more. Will it ever > get 'fixed'? Does it really need to be? Should they or should they not > change it?**** > > It gets a bit philosophical, eh. I don't see this exact bug in the > tracker, so I wonder if this is one of those issues that's been done to > death. Maybe there's a definitive answer out, perhaps it's unimportant? If > not it really should be logged as a bug.**** > > **** > > @Gavin**** > > Your issue is of course related. The cfargument tag defines the keys in > the arguments collection, but your default values are undefined. When the > method is invoked the arguments collection is updated NOT overwritten. This > is also the case when using argumentCollection, ie;**** > > <cfset someMethod(argumentCollection={something='something'})>**** > > A bit annoying? Maybe. Perhaps it has a purpose. I tend to think of it as > a bug. But again it's easy to work around it, as you said just count 'em up > and/or use IsDefined. **** > > On the flip side if it gets 'fixed' it would be more difficult to iterate > over the complete list of arguments. But that could be done using metadata > instead I guess. **** > > (Funnily it's a little like bug 75806 where Sean > Corfield succinctly describes the need for defaults and empty collections > to always be returned when getting metadata.) **** > > Again, my gut feeling is it's a change in behavior that may or may not be > possible and I maybe it's another one that's been discussed a lot and we > missed the party :) **** > > Personally I would just leave out your cfargument tags for that particular > method, you're not losing much.**** > > Finally, just be aware that if your method is invoked with a null value it > is indistinguishable from an omitted argument which could be a real blow to > your design, ie, in CF9 these are the same; **** > > <cfset someMethod()>**** > > <cfset someMethod(messageid=javacast('null',0))>**** > > **** > > @Tof that was pretty cool! **** > > I think in this case, it should probably be more readable. Well, that's my > excuse for such a simple solution, and I'm sticking to it :)**** > > **** > > <cfset local.definedArgumentCount="0">**** > > <cfloop collection="#arguments#" item="arg">**** > > <cfif IsDefined('arguments.' & arg)>**** > > <cfset local.definedArgumentCount++>**** > > </cfif> > </cfloop>**** > > **** > > G'night.**** > > **** > > On 5 January 2012 18:03, christophe albrech <christophe.albr...@gmail.com> > wrote:**** > > Here's a silly but funny way to do it: > > #structCount(arguments) - (listLen(SerializeJSON(arguments),"null") - 1)# > > Dale, with your example, it goes 4 - (3-1) = 2 actual elements. > > Tof**** > > **** > > On Thu, Jan 5, 2012 at 5:21 PM, Kai Koenig <k...@koeni.de> wrote:**** > > I trust you've logged that bug? > > Cheers > Kai**** > > > > > I think it's a bug one way or another and Adobe should fix it. > > > > structCount is INCORRECT imo shows 4 elements > > dump is INCORRECT imo shows 4 elements > > structKeyExists is CORRECT. It doesn't think its there. > > > > Anything else you try to do to those elements, think it doesn't exists > thus > > the first 2 don't follow the same rules > > > > <cfoutput>#arguments.a4#</cfoutput> > > > > Element A4 is undefined in ARGUMENTS. > > > > One way or another the behaviour should be consistant. > > > > Regards > > Dale Fraser > > > > http://dale.fraser.id.au > > http://cfmldocs.com > > http://learncf.com > > http://flexcf.com > > > > > > -----Original Message----- > > From: cfaussie@googlegroups.com [mailto:cfaussie@googlegroups.com] On > Behalf > > Of charlie arehart > > Sent: Thursday, 5 January 2012 4:55 PM > > To: cfaussie@googlegroups.com > > Subject: RE: [cfaussie] Count the number of arguments passed into a > > function. > > > > Yep, that's an interesting challenge, Gavin. (And if anyone may have read > > only the first sentence of his note and not the rest, note that he IS > > already counting the arguments struct, but the issue is that because he's > > using cfargument to define them, even without any default, all those > defined > > args appear in the struct even if no value is passed in.) > > > > Ben Nadel has blogged on this same problem a few years ago: > > > > > http://www.bennadel.com/blog/1430-ColdFusion-ARGUMENTS-Keys-Always-Exist-Eve > > n-When-Not-Required.htm > > > > Sadly neither he nor anyone else reading it had any solution other than > what > > you've proposed, Gavin, doing a structkeyexist to test if there's a value > > passed in. > > > > I do agree that it could be helpful to get the value more easily, and I'm > > really surprised that it's not something that's been solved (though I > have > > to admit I've not needed it before, but I can appreciate its value.) > Perhaps > > someone here will point out something we're all missing. :-) > > > > One little tip that could help find another solution is that the > arguments > > scope can be processed either as a struct or an array. While > > arraylen(arguments) returns the same value as structcount, but the slot > in > > the array for any argument not provided will be empty, and that can be > > tested as of CF8 with arrayisdefined(array,index). Not really any better > > than structkeyexists. Again, though, maybe thinking about it as an array > > instead may give someone an idea. > > > > Finally, I'll note that I searched the cflib.org site, and of the 55 > UDFs > > there that refer to "count", none seemed related to solving this problem. > > Same with the 21 that referred to "arguments". > > > > /charlie > > > > > >> -----Original Message----- > >> From: cfaussie@googlegroups.com [mailto:cfaussie@googlegroups.com] On > >> Behalf Of Gavin Baumanis > >> Sent: Wednesday, January 04, 2012 11:55 PM > >> To: cfaussie > >> Subject: [cfaussie] Count the number of arguments passed into a > >> function. > >> > >> Hi there, > >> Let me explain what I am trying to do, > >> > >> the executive summary is that I want to count the number of arguments > >> passed into a function. > >> anyFunction(), might have 10 cfargument properties assigned to it. > >> But if I call; > >> anyFunction(arg1="1", arg2="2") > >> > >> Then I somehow want to be able to retrieve the value 2 (2 passed in > >> variables). > >> > >> Why, > >> Because I have a getObject method. > >> getObject is basically a cfquery and I filter the results of the > >> query, based on the arguments passed in. > >> so if I have; > >> getObject(myId = 1, yourId = 2); > >> > >> then it creates SQL that looks like; > >> select STUFF > >> from myTable > >> where 1=1 > >> and myid=1 > >> and yourid=1 > >> and...(add a number of arguments here....) > >> > >> If I do NOT pass in any arguments, then the SQL retrieves ALL rows > >> from the table. > >> But I want to do is; > >> If no arguments are passed in - create an empty object. > >> > >> (we have a getAllObjects function to retrieve all rows) > >> > >> maybe some code will help... > >> > >> I have two snippets of code; > >> One is a function in a CFC, the other is some code in a CFM template > >> that calls the function - pretty simple. > >> > >> myTestCFC.cfc: > >> <cffunction name="blah"> > >> <cfargument name="text" required="false" /> > >> <cfargument name="messageid" required="false" /> > >> <cfargument name="queryid" required="false" /> > >> <cfargument name="datasource" required="false" /> > >> > >> <cfset var local = {} /> > >> <cfset local.length = #structCount(arguments)#> > >> > >> <cfreturn local.length> > >> </cffunction> > >> > >> > >> test.cfm: > >> <cfset myReturnVal = myTestCFC.blah(text="gav", queryid=3)> <cfdump > >> var="#myReturnVal#"> <br /> <cfif structKeyExists(messageCFC, > >> "messageid")> > >> Yes > >> <cfelse> > >> No > >> </cfif> > >> > >> the output of this code is; > >> 4 > >> No > >> > >> The value 4 is because blah() has 4 arguments defined. > >> But structKeyExists, returns false... > >> > >> How can it not exist but be accounted for? > >> surely one of the values is wrong? > >> > >> If I change the code slightly to be; > >> <cfset local.length = #structKeyList(arguments)#> instead of; <cfset > >> local.length = #structCount(arguments)#> > >> > >> Then all four arguments defined in the "stub" of the function are > >> listed. > >> > >> So is one of the values being returned a bug? > >> > >> and lastly, > >> How do I go about "counting" actual passed in aerguments? > >> (I could write another function that loops through > >> structKeyLists(arguments), then do a structKeyExists[i] and increment > >> a counter and do my own accounting.... > >> but it just "seems" like there should be a nicer way to do it... > >> > >> As always - Thanks for any thoughts! > >> > >> Gavin. > > > --**** > > Kai Koenig - Ventego Creative Ltd > ph: +64 4 476 6781 - mob: +64 21 928 365 / +61 435 263 414 > web: http://www.ventego-creative.co.nz > blog: http://www.bloginblack.de > twitter: http://www.twitter.com/agentK > --**** > > > > > > > > > -- > You received this message because you are subscribed to the Google Groups > "cfaussie" group. > To post to this group, send email to cfaussie@googlegroups.com. > To unsubscribe from this group, send email to > cfaussie+unsubscr...@googlegroups.com. > For more options, visit this group at > http://groups.google.com/group/cfaussie?hl=en.**** > > **** > > -- > You received this message because you are subscribed to the Google Groups > "cfaussie" group. > To post to this group, send email to cfaussie@googlegroups.com. > To unsubscribe from this group, send email to > cfaussie+unsubscr...@googlegroups.com. > For more options, visit this group at > http://groups.google.com/group/cfaussie?hl=en.**** > > **** > > -- > You received this message because you are subscribed to the Google Groups > "cfaussie" group. > To post to this group, send email to cfaussie@googlegroups.com. > To unsubscribe from this group, send email to > cfaussie+unsubscr...@googlegroups.com. > For more options, visit this group at > http://groups.google.com/group/cfaussie?hl=en.**** > > -- > You received this message because you are subscribed to the Google Groups > "cfaussie" group. > To post to this group, send email to cfaussie@googlegroups.com. > To unsubscribe from this group, send email to > cfaussie+unsubscr...@googlegroups.com. > For more options, visit this group at > http://groups.google.com/group/cfaussie?hl=en.**** > > -- > You received this message because you are subscribed to the Google Groups > "cfaussie" group. > To post to this group, send email to cfaussie@googlegroups.com. > To unsubscribe from this group, send email to > cfaussie+unsubscr...@googlegroups.com. > For more options, visit this group at > http://groups.google.com/group/cfaussie?hl=en.**** > > -- > You received this message because you are subscribed to the Google Groups > "cfaussie" group. > To post to this group, send email to cfaussie@googlegroups.com. > To unsubscribe from this group, send email to > cfaussie+unsubscr...@googlegroups.com. > For more options, visit this group at > http://groups.google.com/group/cfaussie?hl=en. > -- You received this message because you are subscribed to the Google Groups "cfaussie" group. To post to this group, send email to cfaussie@googlegroups.com. To unsubscribe from this group, send email to cfaussie+unsubscr...@googlegroups.com. For more options, visit this group at http://groups.google.com/group/cfaussie?hl=en.