Tuesday, June 10, 2008

Comparing two ColdFusion Objects (object comparison)

The problem I am trying to figure out is if there was a great way in ColdFusion 8 to compare 2 objects just like you would compare 2 strings using a 'Compare' function
For example:
Compare(string1,string2)


I found no such function and so the hunt begun to find a suitable solution.

I tried looking into some under the hood java functionality to help, but settled on another approach that seems to work pretty well, here's how it goes.

Our 'Beans' are all structured where their 'instance' data is in a variable structure i.e. etc.

I also have a generic method in my beans called 'getInstance' which returns a Structure containing all the instance data, it's a simple but powerful method that helps in debugging what data is contained in the Bean at a glance

...cffunction methog="getInstance" returntype="struct"

..cfreturn variables.instance


So I added a 'compareObject' method to my bean where I can pass in a bean of the same type and compare the 2 objects by introspecting the data from the 'getInstance' call on each of them and then simply looping through the structure keys and comparing them -
So regardless of whether the data is a date, numeric or string, the
<!--- Author: penny - Date: 6/9/2008 --->
<cffunction name="compareObject" output="false" access="public" returntype="boolean" hint="Compares 2 objects that have a getInstance Interface defined for them - ">
<cfargument name="ruleBean" type="ruleBean" required="true"/>

<!--- first compare the Structure Keys if that's the same (which they should be if they're the same object so let's ignore) --->
<!--- Next for each key in the Structure, compare the values - if there is one that is a non-match, then return a false and exit --->
<cfset var item = ''/>
<cfset var returnValue = true/>
<cfset var tmpObj =arguments.ruleBean.getInstance() />
<cfloop collection="#variables.instance#" item="item">
<cfif isSimpleValue(variables.instance[item])>
<cfif variables.instance[item] neq tmpObj[item]>
<cfset returnValue = false/>
<cfbreak/>
</cfif>
</cfif>
</cfloop>

<cfreturn returnValue />
</cffunction>


Note: this is calling 'isSimpleValue' to compare only numerics, boolean, datetime, strings and not Structures and Arrays, which simplifies the comparison scope, but may likely solve the 90% rule while leaving it up to you to implement or extend this method to do comparison accross your complex instance data.

So calling this is as simple as instantiating the bean, and calling the compareObject method on it, and passing in the bean you want to compare it to..

<cfset rule = createObject( "component", "ruleBean").init(1)/>
<cfset rule2 = createObject( "component", "ruleBean").init(2)/>
<cfset x =rule.compareObject(rule2) />
<cfdump var="#x#"/>

Returns 'false' as they are not equal (instance data in one object is '1' while the other is '2'.

Until we have a true 'compareObject' function to compare 2 objects, this will have to work for now.

9 comments:

Will Belden said...

Another option would be to have some sort of "complex object to string" thing. You pass it the instance data, it just builds a big fat string out of everything it can. Then you can compare the hash()'es of the two strings. Probably not too disimilar from what you've already done. But you could go a step further. If isObject(someInstanceVariable) that isn't a string, date, numeric, etc. you could have a function that returns a string for it. Perhaps it looks at that instance.object and sees if it has a getInstance() function, too, and so on. Since almost all of my objects have a few basic mixins, like getInstance(), you could easily write the function and add it to the mixin, making it always available. (Wouldn't work for a true COM component, like a DLL, but it's a further expansion idea.)

- Will B.
http://CommonManRants.blogspot.com

Code Fusion, LLC said...

Good thoughts - I think that is really what I was after, but my question is what would the 'function that returns a string for it (complex object)' look like?

I had also toyed with the thought of getting the 'footprint' data of the object somehow by calling it's methods without the 'function notation () parenthesis' and calling the toString() on them.

For Example:

..cfset x = crateobject('component','test')
cfset footprint = x.getmethog.toString()


This may return something that looks like

'cftest2ecfc1673353112$funcGETPATH@1718bf3 '
but as it turns out, this data was the same for 2 similar object, even with different instance data in them.

I was hoping if they did differ in some way, that it may be key to determining 'differences' in the objects. But if i create 2 variables from the same object and to this object.methodname.toString() on it, they both will return the same data.

Anonymous said...
This comment has been removed by a blog administrator.
Perry said...

Just use the toScript function on the object. It creates a javascript version of your complex object. Then do a compare against the 2. Works 100% of the time with all CF objects.

Compare(toScript(obja, "jsVar"), toScript(objb, "jsVar"))

It's the "complex object to string" option.

Zach Stevenson said...

Structs have a java function called Equals which it inherits from hash map in java. To compare 1 struct to another: myStruct.equals(otherStruct)

Components are a little tricky/hacky. Comparing their momento like what you did is ok, however there is an easier way using the equals function from structs. If you wrap your components you are comparing inside structs, you can compare them and it actually does a component comparison.

Jackie Co Kad said...

Thanks for the post, I am techno savvy. I believe you hit the nail right on the head. I am highly impressed with your blog. It is very nicely explained. Your article adds best knowledge to our Java Online Training from India. or learn thru Java EE Online Training Students.

priya said...

Your good knowledge and kindness in playing with all the pieces were very useful. I don’t know what I would have done if I had not encountered such a step like this.
Best Devops online Training
Online DevOps Certification Course - Gangboard
Best Devops Training institute in Chennai

tamizh said...

I appreciate that you produced this wonderful article to help us get more knowledge about this topic.
I know, it is not an easy task to write such a big article in one day, I've tried that and I've failed. But, here you are, trying the big task and finishing it off and getting good comments and ratings. That is one hell of a job done!



Selenium training in bangalore
Selenium training in Chennai
Selenium training in Bangalore
Selenium training in Pune
Selenium Online training

pragya pragya said...

Nice tutorial. Thanks for sharing the valuable information. it’s really helpful. Who want to learn this blog most helpful. Keep sharing on updated tutorials…
python Course in Pune
python Course institute in Chennai
python Training institute in Bangalore