Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
I am the chief technical officer at InVision App, Inc - a prototyping and collaboration platform for designers, built by designers. I also rock out in JavaScript and ColdFusion 24x7.
Meanwhile on Twitter
Loading latest tweet...
Ben Nadel at cf.Objective() 2012 (Minneapolis, MN) with: Matt Graf

Comparing ColdFusion Struct Equality With Java

By Ben Nadel on
Tags: ColdFusion

I am really loving this whole Java thing. Have you ever wanted to compare two different Structs in ColdFusion? Maybe do something like:

  • <!--- Set up one struct. --->
  • <cfset objGirlA = StructNew() />
  • <cfset objGirlA.Name = "Sarah" />
  • <cfset objGirlA.IsHottie = "Yes" />
  • <cfset objGirlA.Measurements = ListToArray( "36,28,36" ) />
  •  
  • <!--- Set up another struct. --->
  • <cfset objGirlB = StructNew() />
  • <cfset objGirlB.Name = "Sarah" />
  • <cfset objGirlB.IsHottie = "Yes" />
  • <cfset objGirlB.Measurements = ListToArray( "36,28,36" ) />
  •  
  • <!--- Test for equality. --->
  • <cfset blnIsEqual = (objGirlA EQ objGirlB) />

If you ever have, then you know this throws the following error:

Complex object types cannot be converted to simple values. The expression has requested a variable or an intermediate expression result as a simple value, however, the result cannot be converted to a simple value. Simple values are strings, numbers, boolean values, and date/time values. Queries, arrays, and COM objects are examples of complex values.

No such luck. However, if you grab the Java methods on ColdFusion structs, such a thing is indeed possible. Structs have the Java method, Equals():

  • <cfset blnIsEqual = objGirlA.Equals( objGirlB ) />

This compares the passed struct, objGirlB, to the calling struct, objGirlA. And, you guessed it, it works just fine.

There are, however, some caveats. You can only compare simple values. Well, that's not exactly true; you can only compare values that are passed by value (not by reference), from what I can see. This includes numbers, strings, dates, and even arrays. In the above example, the Measurements key gets compared successfully even though it is an array and considered complex. If you have any keys that have components or Java objects, they are not compared; it doesn't throw an error, but I don't think any comparison is taking place. For instance, these two structs:

  • <!--- Create one with hotness 10. --->
  • <cfset objGirlA = StructNew() />
  • <cfset objGirlA.Name = "Sarah" />
  • <cfset objGirlA.Hotness = CreateObject( "component", "Girl" ).Init(
  • hotness = 10
  • ) />
  •  
  • <!--- Create one with hotness 9. --->
  • <cfset objGirlB = StructNew() />
  • <cfset objGirlB.Name = "Sarah" />
  • <cfset objGirlB.Hotness = CreateObject( "component", "Girl" ).Init(
  • hotness = 9
  • ) />

 

... when compares are evaluated as the same. The differences in hotness (10 vs. 9) does not get taken into account.

Anyway, not sure how useful this is, but sort of nifty little thing.


Looking For A New Job?

100% of job board revenue is donated to Kiva. Loans that change livesFind out more »

Reader Comments

Be careful when using this technique. There are some strange quirks with using equals() with ColdFusion numbers. For instance:

<cfset a = structNew() />
<cfset a.n = 2 />

<cfset b = structNew() />
<cfset b.n = 2 />

<cfoutput>
#a.equals(b)#
</cfoutput>

The above outputs "YES" as expected. But if we change b.n to 1+1:

<cfset a = structNew() />
<cfset a.n = 2 />

<cfset b = structNew() />
<cfset b.n = 1 + 1 />

<cfoutput>
#a.equals(b)#
</cfoutput>

it now outputs "NO"!

Reply to this Comment

@Tony,

Hmm, very interesting. I never ended up using this anywhere; but it's good to know about these issues. Thanks.

Reply to this Comment

This is a great tip, thanks! Was already thinking about first converting the structs to wddx, but that didn't feel right. Will use this a lot, I guess!

Reply to this Comment

@Tony Wu

If you change it to <cfset a.n = val(2) /> in the second example you get a yes. It has to do with cf implicit type conversion.

Reply to this Comment

@Tony Wu

In your example, the reason for the "NO" is because a.n is created as a java.lang.String and b.n is created as a java.lang.Double - eg.

<cfset a = structNew()>
<cfset a.n = 2>

<cfset b = structNew()>
<cfset b.n = 1 + 1>

<cfoutput>
#a.equals(b)# (NO)
#a.n.getClass().getName()# (java.lang.String)
#b.n.getClass().getName()# (java.lang.Double)
</cfoutput>

to force a.n to be a Double, <cfset a.n = 2 + 0>
to force b.n to be a String, <cfset b.n = 1 + 1 & "">

You could bounce the a and b structs through serializeJSON and deserializeJSON, which makes both numbers java.lang.Strings, but has other side effects - wouldn't recommend it...

Doubles and Longs, when compared, return true
eg.

<cfset c = structNew()>
<cfset c.n = createObject( 'java', 'java.lang.Long').init(2)>

<cfset d = structNew()>
<cfset d.n = 1 + 1>

<cfoutput>
#c.equals(d)# (YES)
#c.n.getClass().getName()# (java.lang.Double)
#d.n.getClass().getName()# (java.lang.Long)
</cfoutput>

I am guessing this is the case as both Double and Long extend the java.lang.Number class. If that's the case, Short, Integer, Float, BigInteger and BigDecimal would also return true when compared (and of course the values are the same)

Reply to this Comment

Looks like others have hit the same issue I've just hit, having to do with Boolean variables. I was attempting to serialize a structure to JSON, then deserialize the data and compare the deserialized version to the original structure, like this:

<cfset myStruct = {
bla = "hello",
ble = 5,
blo = true
}>

<cfset myJSON = deserializeJSON(serializeJSON(myStruct))>
<cfoutput>#myStruct.Equals( myJSON )#</cfoutput>

Equals() in this case returns NO, because the boolean value for "blo = true" gets converted to "YES" during the JSON round-trip. If I pull that variable out of the structure, then Equals() returns true.

Really annoying, and sort of a show-stopper. Unless I'm missing something.

Reply to this Comment

I should follow up and say that perhaps this post wasn't the right place for my comment, given that it's really a problem with how CF handles JSON, and not with the Equals() function. Had I done a little more Googling prior to posting, I would have seen that the CF community has its fair share of issues with CFJSON.

The one new wrinkle for me, though, is that the JSON round-trip took something that was a boolean and converted it to a string. I've seen others complain about the opposite: CFJSON taking a "yes" or "no" string value and converting it to a boolean. So it's weird that CFJSON manages to get both directions wrong.

Of course, I'm happy to be misinformed on this front, so if anybody can tell me what I'm doing wrong, if it's something obvious, I'd be glad for the education.

Reply to this Comment

I had the same issue - there are several times that the java "equals" method does not return the expected value.

On researching this, I found this User Defined Function by Milan Chandna that defines 3 complex compare functions - structCompare(St1,St2), arrayCompare(arr1,arr2) and queryCompare(q1,q2).

The file is called complexCompare.cfm, and is available on his blogspot at

http://milanchandnacf.blogspot.com/2012/02/equate-arrays-structs-query-or-possible.html

I ended up using this, and it works more reliably than the .equals method....in my case, I retrieved a complex structure from a data table with many associated tables, creating a structure with arrays of structures in them. I then made a copy of the original structure in session variables, and used the original structure as values in a form, allowing the users to edit these values. In the file handling the submission of the form (where the original structure was lost), I re-created the structure using the form field variables.

Even when I made no changes to the various fields in the form, the .equals method returned FALSE all the time, while the structCompare function returned true....

I hope this helps....

Vinay Mehta.

P.S. - Ben, I have been lurking and learning for a couple of years now...I am glad to be able to finally contribute!

Reply to this Comment

Post A Comment

You — Get Out Of My Dreams, Get Into My Comments
Live in the Now
Oops!
Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.