Last active
August 29, 2015 14:22
-
-
Save Drumsticks/6ea1ba631c8198e7135a to your computer and use it in GitHub Desktop.
reStructFindValue - Adding regular expresssion searching to structFindValue
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* @hint I search for patterns within a given | |
* @output false | |
* | |
* @target I am the target struct being searched. | |
* @pattern I am the pattern being searched. | |
* @scope I am the scope of the search: one or all. | |
* @path 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. | |
* | |
* Reference: http://www.bennadel.com/blog/1635-restructfindvalue-adding-regular-expression-searching-to-structfindvalue.htm | |
*/ | |
public array function reStructFindValue( | |
required any target, | |
required string pattern, | |
string scope = 'one', | |
string path = '' | |
) { | |
// Define the local scope. | |
var local = {}; | |
// Create an array | |
local.results = []; | |
/* | |
RH (Edit) - Replaced the following custom tag with Ben Nadel's arrayCollection(), so there is no need for the custom tag. Reference: http://www.bennadel.com/blog/2073-creating-a-struct-from-a-coldfusion-array-using-the-treemap-and-the-linkedhashmap.htm | |
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 | |
*/ | |
local.targets = isArray(arguments.target) ? arrayCollection(arguments.target) : arguments.target; | |
for(local.item in local.targets) { | |
// Create a variable to store the base path. | |
local.path = arguments.path; | |
// Add the current key to the path. | |
local.path &= '["#local.item#"]'; | |
// Get a handle on the new target. | |
local.target = local.targets[local.item]; | |
// Check to see if this new target is a string (or | |
// if it is another complex object that we need to | |
// iterate over). | |
if (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. | |
*/ | |
local.matchPosition = reFindNoCase(arguments.pattern, local.target); | |
if (local.matchPosition) { | |
/* | |
The regular expression patther was found at | |
least once in the target value. This is a | |
valid match. Add it to the results. | |
*/ | |
local.result = {key = local.item, | |
owner = arguments.target, | |
path = local.path, | |
position = local.matchPosition}; | |
// Add this result to the current results. | |
arrayAppend(local.results, local.result); | |
} | |
} else if ( isStruct(local.target) | |
|| isArray( local.target)) { | |
/* | |
Make sure this complex nested target is one that | |
we can actually iterate over (all others will be | |
skipped). | |
*/ | |
/* | |
The nested taret is not a simple value. Therefore, | |
we need to perform a depth-first, recusive search | |
of it for our matching pattern. | |
*/ | |
local.childResults = reStructFindValue( local.target, | |
arguments.pattern, | |
arguments.scope, | |
local.path); | |
/* | |
Add the results from our nested search to the | |
current results collection. | |
*/ | |
//writeOutput('here'); abort; | |
//writeDump(var=local.childResults, abort=true); | |
for (local.childResult in local.childResults) { | |
// Add this result to the current results. | |
arrayAppend(local.results, local.childResult); | |
} | |
} | |
/* | |
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. | |
*/ | |
if (arguments.scope == "one" && arrayLen(local.results)) { | |
/* | |
We found at least one item - trim the results | |
set in case the last iteration found more than | |
one. | |
*/ | |
local.trimmedResults = [local.results[1]]; | |
// Return the trimmed result set. | |
return local.trimmedResults; | |
} | |
} | |
// Return the found results. | |
return local.results; | |
} | |
/** | |
* @hint I return a the given array as a collection of array keys in natural order. In order to maintain proper numeric ordering, the keys are zero-padded to all be the same length. | |
* @output false | |
* | |
* @array I am the array for which we are getting the key collection. | |
*/ | |
public function arrayCollection( | |
required array array | |
) { | |
// Define the local scope. | |
var local = {}; | |
/* | |
Create our key collection. By using Java's TreeMap, we | |
will provide struct-like behavior in which the key iterator | |
returns keys in a natural order - alphabetically as strings. | |
We will need to prefix the values to make sure the return | |
in the correct order. | |
*/ | |
local.keys = createObject('java', 'java.util.TreeMap').init(); | |
/* | |
Beacuse the default sorting of the TreeMap is alphetical, it | |
will cause a problem for our numeric keys. We *could* write | |
a numeric comparator in Java - but, for this demo, we will | |
just be zero-padding to normalize the alpha-numeric gap. | |
Calculate the zero-padding that we need to supply for the | |
index as we add it to the map. | |
*/ | |
local.zeroPadding = repeatString( | |
"0", | |
len(arrayLen(arguments.array)) | |
); | |
/* | |
Get the character width we want to limit the key length to. | |
This is to help us order them in natural order. | |
*/ | |
local.keyLength = len(local.zeroPadding); | |
/* | |
Loop over the arrays indicies to add them to the key map as | |
index-value pairs. | |
*/ | |
for (local.i = 1; local.i <= arrayLen(arguments.array); local.i++) { | |
// Check to see if the current index is defined. | |
if (arrayIsDefined(arguments.array, local.i)) { | |
/* | |
Add the index to the map. As we do this, we are going | |
to left-zero-pad the index values to make the all | |
strings of the same order. | |
*/ | |
local.keys.put( | |
javaCast( | |
'string', | |
right( | |
(local.zeroPadding & local.i), | |
local.keyLength | |
) | |
), | |
arguments.array[local.i] | |
); | |
} | |
} | |
// Return the array collection. | |
return local.keys; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment