<cffunction name="reStructFindValue" access="public" returntype="array" output="false" hint="I search for patterns within a given "> <!--- Define arguments. ---> <cfargument name="target" type="any" required="true" hint="I am the target struct being searched." /> <cfargument name="pattern" type="string" required="true" hint="I am the pattern being searched." /> <cfargument name="scope" type="string" required="false" default="one" hint="I am the scope of the search: one or all." /> <cfargument name="path" type="string" required="false" default="" hint="The path to the current target (for recursive calling). ** NOTE: This is used internally for recursion - this is NOT an expected argument to be passed in by the user." /> <!--- Define the local scope. ---> <cfset var local = {} /> <!--- Create an array ---> <cfset local.results = [] /> <!--- Loop over target. NOTE: This uses a ColdFusion custom tag that unifies the interface for looping over both structure and arrays. http://www.bennadel.com/go/each-iteration ---> <cf_each item="local.item" collection="#arguments.target#"> <!--- Create a variable to store the base path. ---> <cfset local.path = arguments.path /> <!--- Add the current key to the path. ---> <cfset local.path &= "[ ""#local.item.key#"" ]" /> <!--- Get a handle on the new target. ---> <cfset local.target = local.item.value /> <!--- Check to see if this new target is a string (or if it is another complex object that we need to iterate over). ---> <cfif isSimpleValue( local.target )> <!--- Check it for the pattern match on the target value. For now, we are going to be using ColdFusion's Match() method which means a sub set of regular expression usage. Furthermore, we are going to use NoCASE for each of coding. ---> <cfif arrayLen( reMatchNoCase( arguments.pattern, local.target ) )> <!--- The regular expression patther was found at least once in the target value. This is a valid match. Add it to the results. ---> <cfset local.result = { key = local.item.key, owner = arguments.target, path = local.path } /> <!--- Add this result to the current results. ---> <cfset arrayAppend( local.results, local.result ) /> </cfif> <!--- Make sure this complex nested target is one that we can actually iterate over (all others will be skipped). ---> <cfelseif ( isStruct( local.target ) || isArray( local.target ) )> <!--- The nested taret is not a simple value. Therefore, we need to perform a depth-first, recusive search of it for our matching pattern. ---> <cfset local.childResults = reStructFindValue( local.target, arguments.pattern, arguments.scope, local.path ) /> <!--- Add the results from our nested search to the current results collection. ---> <cfloop index="local.childResult" array="#local.childResults#"> <!--- Add this result to the current results. ---> <cfset arrayAppend( local.results, local.childResult ) /> </cfloop> </cfif> <!--- At the end of a single iteration, let's check to see if we were only searching for one target. If we are, AND we found it, we can simply return the single element rather than continuing on with our recursion. ---> <cfif ( (arguments.scope eq "one") && arrayLen( local.results ) )> <!--- We found at least one item - trim the results set in case the last iteration found more than one. ---> <cfset local.trimmedResults = [ local.results[ 1 ] ] /> <!--- Return the trimmed result set. ---> <cfreturn local.trimmedResults /> </cfif> </cf_each> <!--- Return the found results. ---> <cfreturn local.results /> </cffunction>