Utility Function: StructMerge

If all you need to do is merge two simple structs, then StructAppend() is a great function to get you there, but if you have complex structs (meaning structs that contain structs) you may be wanting to find a solution that does a deep merge:

<cffunction name="StructMerge" access="public" output="false" returntype="struct">
    <cfargument name="Struct1" type="struct" required="true" />
    <cfargument name="Struct2" type="struct" required="true" />
    <cfargument name="Overwrite" type="boolean" required="false" default="true" />
    
    <!--- Loop Keys --->
    <cfloop collection="#Arguments.Struct2#" item="Local.Key">
        <!--- Find if the new key from Struct2 Exists in Struct1 --->
        <cfif StructKeyExists(Arguments.Struct1, Local.Key)>
            <!--- If they are both structs, we need to merge those structs, too --->
            <cfif IsStruct(Arguments.Struct1[Local.Key]) AND IsStruct(Arguments.Struct2[Local.Key])>
                <!--- Recursively call StructMerge to merge those structs --->
                <cfset StructMerge(Arguments.Struct1[Local.Key], Arguments.Struct2[Local.Key], Arguments.Overwrite) />
            <!--- We already checked that the key existed, now we just check if we can overwrite it --->
            <cfelseif Arguments.Overwrite>
                <cfset Arguments.Struct1[Local.Key] = Arguments.Struct2[Local.Key] />
            <!--- The unused case here is if Overwrite is false, in which case Struct1 is not changed --->
            </cfif>
        <!--- If it doesn't exist, you're free to merge --->
        <cfelse>
            <cfset Arguments.Struct1[Local.Key] = Arguments.Struct2[Local.Key] />
        </cfif>
    </cfloop>
    
    <cfreturn Arguments.Struct1 />
</cffunction>

This code merges all sub structures as well as the core one. I'd also say it would be an easy adjustment to merge arrays and other data types if necessary. I'm not sure if CF10 will be adding something like this or not, but I know for CF9.0.1 I needed it.

 

Comments

Raymond Compton's Gravatar Nice - I wrote an almost identical udf some time ago. Pretty cool how similar both techniques are. I actually chose to have mine to return values like StructAppend() does. Works the same in the end tho.

http://www.cflib.org/udf/structBlend
Baz's Gravatar Perfectly implemented, thanks!
Anthony's Gravatar cfscript version :

public struct function structMerge(
      required struct destStruct,
      required struct sourceStruct,
      boolean overwrite = true
   )
   {
      // Loop Keys
      for( local.key in arguments.sourceStruct ) {
         // Find if the new key from sourceStruct Exists in destStruct
         if( structKeyExists( arguments.destStruct, local.key) ) {
            // If they are both structs, we need to merge those structs, too
            if( isStruct( arguments.destStruct[ local.key ] ) and isStruct(arguments.sourceStruct[local.key] ) ) {
               // Recursively call StructMerge to merge those structs
               structMerge( arguments.destStruct[ local.key ], arguments.sourceStruct[ local.key ], arguments.overwrite )
            }
            // We already checked that the key existed, now we just check if we can overwrite it
            else if( arguments.overwrite ) {
               arguments.destStruct[ local.key ] = arguments.sourceStruct[ local.key ];
            }
            // The unused case here is if Overwrite is false, in which case destStruct is not changed
         }
         // If it doesn't exist, you're free to merge
         else {
            arguments.destStruct[ local.key ] = arguments.sourceStruct[ local.key ];
         }
      }

      return arguments.destStruct;
   }
Jon Hartmann's Gravatar Thanks Anthony - seems like a feature that would be built in by now :)
Comments are not allowed for this entry.
Jon Hartmann, July 2011

I'm Jon Hartmann and I'm a Javascript fanatic, UX/UI evangelist and former ColdFusion master. I blog about mysterious error messages, user interface design questions, and all things baffling and irksome about programming for the web.

Learn more about me on LinkedIn.