structFilter() UDF - Filtering a structure based upon a regular expression

Categories: HTML/ColdFusion

Today I thought I'd share a little ColdFusion helper function I've been using for years: structFilter(). The structFilter() parses a structure and returns only the keys that match a given regular expression.

<!---
 Searches a structure for keys matching a regex and returns a structure containing the
 matched keys.

 @param input        The structure to filter. (struct; required)
 @param regex        The regular expression to filter keys against. (string; required)
 @param useNoCase    Do a case insensitive search? (boolean; default = true)
 @param useDeepCopy  Do a deep copy on the keys? (boolean; default = false)

 @return Returns a structure containing the matching keys.
 @author Dan G. Switzer, II (dan.switzer@givainc.com)
 @version 1, June 17, 2009
--->
<cffunction name="structFilter" returntype="struct" output="false" access="public" 
    hint="Filters keys in a structure to those matching the regular expression.">
  <!---// define valid arguments //--->
  <cfargument name="input" type="struct" required="true" />
  <cfargument name="regex" type="string" required="true" />
  <cfargument name="useNoCase" type="boolean" required="false" default="true" />
  <cfargument name="useDeepCopy" type="boolean" required="false" default="false" />
  
  <!---// define local variables //--->
  <cfset var result = structNew() />

  <!---// if using NoCase and the (?i) directive doesn't exist, append to regex //--->  
  <cfif arguments.useNoCase and not reFind("^\(\?[mx]*i[mx]*\)", arguments.regex)>
    <cfset arguments.regex = "(?i)" & arguments.regex />
  </cfif>

  <cfloop item="key" collection="#arguments.input#">
    <cfif reFind(arguments.regex, key)>
      <cfif arguments.useDeepCopy>
        <cfset result[key] = duplicate(arguments.input[key]) />
      <cfelse>
        <cfset result[key] = arguments.input[key] />
      </cfif>
    </cfif>
  </cfloop>

  <!---// return the new structure //--->  
  <cfreturn result />
</cffunction>

I've found this helper UDF to be extremely handy in when you have a bunch of serialized form fields that follow a given nomenclature. For example, if you have a bunch of form fields like:

<input type="text" name="name" value="" />
<input type="text" name="description" value="" />

<!---// dynamic list of variables //--->
<cfloop index="i" from="1" to="10">
  <cfoutput>
    <input type="text" name="id_#i#" value="" />
  </cfoutput>
</cfloop>

Using the structFilter() UDF you can vary easily process all the fields starting with "id_" by doing:

<!---// 
  get all the fields:
  * starting with "id_"
  * and ending with one or more numbers
//--->  
<cfset stIds = structFilter(form, "^id_\d+$") />

This will give you a structure containing all the "id_" fields that you can then process in a collection-based loop. This method is much more effective then passing in some kind of hidden "counter" variable where you loop from 1 to the counter variable (which is a technique I've used in the past) as it doesn't rely on the numbers being serialized.

There are obviously many other uses for this function, but I use it a lot for processing complex forms that have dynamically generated fields that have a fixed nomenclature.

Comments

Alan McCollough's Gravatar That is mega-slick. It's like one of those profound bash script one-liners. Such a clean solution to the "how do I pick out the form fields with the embedded ID #s in em?" puzzle.
paul Klinkenberg's Gravatar Very nice script indeed! Didn't even know about the (?i) flag option in CF regexes. Will use it in my Railo setup as a core function.

Add Comment

Leave this field empty


If you subscribe, any new posts to this thread will be sent to your email address.