4

I have a nested struct like so struct1.struct2.foo. I would like to check if foo exists. However, struct2 isn't guaranteed to exists either. I loathe to use isDefined(), but I also think that calling structKeyExists() twice is wasteful (e.g., if (structKeyExists(struct, 'struct2') && structKeyExists(struct.struct2, 'foo')) {}

I thought about using structFindKey(), but then I don't want to run into an issue if there exists struct1.foo

Is there a better way to accomplish this?

This is a similar question to this question, but I am not dealing with an XML document so most of the answers in that post doesn't work for me.

2
  • 3
    "I also think that calling structKeyExists() twice is wasteful" - it's not wasteful to do something you need to do. See stackoverflow.com/questions/13914841/… for how to simplify the code. Commented Feb 19, 2013 at 23:06
  • The functions in the other functions are very useful, but overkill for this particular case. I'm just going to use structKeyExists() twice. Commented Feb 21, 2013 at 14:57

3 Answers 3

4

This is the same question (though more succinctly put) that lies at the heart of an earlier question:

How to dynamically loop through an array of structures

I would offer the same answer.

How to dynamically loop through an array of structures

To repeat the essential part, the following function should do what you want:

 <cffunction name="StructGetByKeyList">
      <cfargument name="struct">
      <cfargument name="key">

      <cfif StructKeyExists(struct,ListFirst(key,"."))>
           <cfif ListLen(key,".") GT 1>
                <cfreturn StructGetByKeyList(struct[ListFirst(key,".")],ListRest(key,"."))>
           <cfelse>
                <cfreturn struct[key]>
           </cfif>
      <cfelse>
           <cfreturn "">
      </cfif>
 </cffunction>

Then you could just call StructGetByKeyList(struct1,"struct2.foo") and it would return the string for the key if it exists and an empty string if it does not.

To return a boolean instead, use the following:

<cffunction name="StructNestedKeyExists">
    <cfargument name="struct">
    <cfargument name="key">

    <cfif StructKeyExists(struct,ListFirst(key,"."))>
        <cfif ListLen(key,".") GT 1>
            <cfreturn StructNestedKeyExists(struct[ListFirst(key,".")],ListRest(key,"."))>
        <cfelse>
            <cfreturn true>
        </cfif>
    <cfelse>
        <cfreturn false>
    </cfif>
</cffunction>
Sign up to request clarification or add additional context in comments.

Comments

4

Nothing wrong with using isDefined("struct1.struct2.foo"). It's not as awfully slow as you think. Start with a scope, if you want to make it a bit faster, like "variables.struct1.struct2.foo"

ColdFusion 9 CFML Reference (PDF)

Comments

2

isDefined() could potentially return misleading results. It's not as accurate as structKeyExists(). And unless you're iterating through this code thousands of times at a pop, you won't notice any performance difference. They both perform pretty well. But if the key doesn't exist, that would be where you might notice the difference (again, only with thousands of iterations). isDefined() will traverse through the various available scopes if it can't find it on the first pass. Even if you passed it a variable that looked like it specified the scope you wanted to check. structKeyExists() is pretty explicit in where it looks for that key. If it doesn't find it, it will stop and return FALSE. It might look like you're doing unnecessary work, but it's returning a much more accurate result. And reading the code gives you a pretty good idea of what you're looking for, so you don't have to worry about it not being clean.

Peter's suggestion was a pretty good one. Check out that link.

6 Comments

How could isDefined() be returning misleading results?
Because CF allows you to use dots in a variable name, you could see the issue if you did something strange like creating variables.form.myVar and passing a form with a myVar field. <cfset variables.form.myVar = "This is my var" /> ... more code here... <form ..... > <input type="text" name="myVar" value="This is my form var" /> <input type="submit" ... > </form> <cfif isDefined("FORM.myVar")> Which FORM.myVar gets output here? <cfoutput>#FORM.myVar#</cfoutput> </cfif> On the first load of this page, myVar from the FORM wouldn't exist. But isDefined() would still return TRUE.
Thanks Shawn. The programmer who uses variables[anyScopeName] should be penalized. :)
That's kind of an oversimplified example, and doing something like that would be bad coding. But my point is that you CAN do that. It's easy to see the error on a short page like that, but what if that FORM is included in a deeply nested <cfinclude>? That would be where it would be easy to miss a mistake like that. And you'd never output the FORM value because the VARIABLES scope will get evaluated before the FORM scope.
@Henry I would COMPLETELY agree. If you use a reserved word or a scope name in your variable, you should be smacked. And you deserve the debugging headache that you'll create for yourself. :-)
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.