Skip to content

Instantly share code, notes, and snippets.

@Drumsticks
Last active August 29, 2015 14:22
Show Gist options
  • Save Drumsticks/6ea1ba631c8198e7135a to your computer and use it in GitHub Desktop.
Save Drumsticks/6ea1ba631c8198e7135a to your computer and use it in GitHub Desktop.
reStructFindValue - Adding regular expresssion searching to structFindValue
/**
* @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