2010-06-30 15 views
8

(Dies ist eine Coldfusion-Frage)Wie kann ich einen "tiefen Vergleich" oder "Diff" auf zwei Structs machen?

Ich habe zwei verschiedene Strukturen, die die gleichen Daten enthalten können oder nicht, und ich möchte in der Lage sein zu sehen, ob sie es tun! My Structs enthält immer einfache Werte (Numbers, Strings oder Booleans), weil sie mit DeserializeJSON erstellt werden, also kann dies hoffentlich leicht gemacht werden.

Ich fand Ben Nadel Post here, aber diese Technik scheint nicht für mich arbeiten. Hier ist, was ich bisher versucht (einige cfwheels Code in dort):

itemA = DeSerializeJSON(model("itemsnapshot").findByKey(4).json); 
itemB = DeSerializeJSON(model("itemsnapshot").findByKey(5).json); 

StructDelete(itemA,"updatedAt"); 
StructDelete(itemB,"updatedAt"); 
StructDelete(itemA,"createdAt"); 
StructDelete(itemB,"createdAt"); 

writedump(itemA); 
writedump(itemB); 

out = itemA.Equals(itemB); 
writedump(out); 

Und die Ergebnisse, dass wie folgt aussehen:

Struct 
code string C112 
companyid number 1 
cost number 5000 
deletedAt string 
description string Nightstand 
id number 70634 
itemtypeid string 13 
projectid number 8 
unittypeid string 

Struct 
code string C112 
companyid number 1 
cost number 5000 
deletedAt string 
description string Nightstand 
id number 70634 
itemtypeid string 13 
projectid number 8 
unittypeid string 

boolean false 

so, wie Sie oben sehen werden, auch wenn die Daten innerhalb der Structs scheinen genau zu entsprechen, dass sie den Test Equals() nicht bestehen.

Hat jemand anderes dies erfolgreich gemacht?

+0

hmm .. Tippfehler? Denn "id Nummer 70634" und "idnumber 70634" sind nicht gleich – Henry

+0

ja das war ja ein Tippfehler! –

Antwort

9

Hier Bens Lösung schnell auf meine Bedürfnisse angepasst, können Sie es weiter anpassen können (und es hoffentlich pretier machen):

<cffunction name="DiffStructs" hint="Compute the differences between two structures" access="public" output="true" returntype="array" > 
     <cfargument name="First" type="struct" required="true" /> 
     <cfargument name="Second" type="struct" required="true" /> 
     <cfargument name="ignoreMissing" type="boolean" required="false" default="false" /> 
     <cfargument name="ignoreFirstEmptyString" type="boolean" required="false" default="false" /> 
     <cfargument name="ignoreSecondEmptyString" type="boolean" required="false" default="false" /> 

     <cfset var Result = arrayNew(1) > 
     <cfset var Keys = structNew() > 
     <cfset var KeyName = "" > 
     <cfset var obj = "" > 
     <cfset var firstOk = true > 
     <cfset var secondOk = true > 

     <cfloop collection="#Arguments.First#" item="KeyName"> 
       <cfset Keys[KeyName]=1> 
     </cfloop> 
     <cfloop collection="#Arguments.Second#" item="KeyName"> 
       <cfset Keys[KeyName]=1> 
     </cfloop> 
     <cfloop collection="#Keys#" item="KeyName"> 
      <cfif NOT StructKeyExists(Arguments.First, KeyName) > 
        <cfif NOT arguments.ignoreMissing> 
         <cfif structFind(Arguments.Second, KeyName) neq ""> 
          <cfif arguments.ignoreSecondEmptyString> 
           <cfset obj = { key = KeyName 
               ,old = "" 
               ,new = structFind(Arguments.Second, KeyName) } > 
           <cfset arrayAppend(Result, obj)> 
          </cfif> 
         </cfif> 
        </cfif> 

      <cfelseif NOT StructKeyExists(Arguments.Second, KeyName)> 
        <cfif NOT arguments.ignoreMissing> 
         <cfif structFind(Arguments.First, KeyName) neq ""> 
          <cfif arguments.ignoreFirstEmptyString > 
           <cfset obj = { key = KeyName 
               ,old = structFind(Arguments.First, KeyName) 
               ,new = "" } > 
           <cfset arrayAppend(Result, obj)> 
          </cfif> 
         </cfif> 
        </cfif> 

      <cfelseif Arguments.First[KeyName] NEQ Arguments.Second[KeyName] > 

       <cfset firstOk = true > 
       <cfset secondOk = true > 

       <cfif structFind(Arguments.Second, KeyName) eq ""> 
        <cfif arguments.ignoreSecondEmptyString> 
         <cfset firstOk = false > 
        </cfif> 
       </cfif> 

       <cfif structFind(Arguments.First, KeyName) eq ""> 
        <cfif arguments.ignoreFirstEmptyString> 
         <cfset secondOk = false > 
        </cfif> 
       </cfif> 

       <cfif firstOk AND secondOk > 
        <cfset obj = { key = KeyName 
            ,old = structFind(Arguments.First, KeyName) 
            ,new = structFind(Arguments.Second, KeyName) } > 
        <cfset arrayAppend(Result, obj)> 
       </cfif> 
      </cfif> 

     </cfloop> 

     <cfreturn Result> 
    </cffunction> 
+0

danke für die schnelle Antwort - das ist sehr nah an dem, was ich brauche! –

+0

Ich denke, Sie wollen 'NOT arguments.ignoreFirstEmptyString' in Zeile 25 und' NOT arguments.ignoreSecondEmptyString' in Zeile 37. WIE IST, wenn ein Schlüssel in der einen Struktur existiert, aber nicht in der anderen, wird es nicht zurückgegeben. Wenn Sie das 'NOT' verwenden, wird der Schlüssel zurückgegeben, wenn er sich in einer Struktur befindet und nicht in der anderen. – kralco626

4

Wenn Sie mit CF9 oder Railo 3

ArrayContains([struct1], struct2); //case-sensitive 

oder

ArrayFindNoCase([struct1], struct2)); //case-insensitive, 0 if not the same. 
ArrayContainsNoCase([struct1], struct2); // if you use Railo 
+0

tolle Infos. Obwohl ich versuchte, in meiner Frage einen einfachen Vergleich zu true oder false zu machen, hat das diff-Array, das von zarkos Funktion generiert wurde, eine Änderung in meiner Schnittstelle ausgelöst, die viel nützlichere Funktionalität bieten wird.Ich könnte einen dieser Tests verwenden, um eine erste Überprüfung durchzuführen, bevor ich das volle Diff ausführe. –

3

In Coldfusion Structures versteckt ist eine handliche kleine Methode namens hashCode(). Bedenken Sie, dass dies nicht dokumentiert ist.

<cfif struct1.hashCode() Eq struct2.hashCode()> 

</cfif> 
+0

interessant! Weißt du, ob das auch in Railo funktioniert? –

+1

Nein, habe es einfach auf Railo ausprobiert und der hashCode-Wert ist immer anders. Obwohl ... st1.toString() Eq st2.toString() funktioniert auf Railo;) –

1

Sie können dies auch mit der systemeigenen Java-Methode durchführen, die vom CFC übernommen wurde.

isThisTrue = ObjA.equals(ObjB); 
+2

beachten Sie, dass dies die Technik ist, die ich in der ersten Post demonstriert. mit meinen zwei Strukturen, die ähnlich erscheinen, ergibt es "falsch". equals() funktioniert nicht in allen Fällen. –

0

Hier ist etwas, was ich schnell zusammen geworfen habe. Es gibt Parameter, um zu bestimmen, ob die Werte und Schlüssel zwischen Groß- und Kleinschreibung unterschieden werden sollen. Werfen Sie diese beiden Funktionen (StructEquals(), ArrayEquals()) in eine Art Utilities CFC.

Einschränkung: Funktioniert nicht für Strukturen/Arrays, die Abfragen oder Objekte enthalten.

<cffunction name="StructEquals" access="public" returntype="boolean" output="false" 
      hint="Returns whether two structures are equal, going deep."> 
    <cfargument name="stc1" type="struct" required="true" hint="First struct to be compared." /> 
    <cfargument name="stc2" type="struct" required="true" hint="Second struct to be compared." /> 
    <cfargument name="blnCaseSensitive" type="boolean" required="false" default="false" hint="Whether or not values are compared case-sensitive." /> 
    <cfargument name="blnCaseSensitiveKeys" type="boolean" required="false" default="false" hint="Whether or not structure keys are compared case-sensitive." /> 
    <cfscript> 
    if(StructCount(stc1) != StructCount(stc2)) 
     return false; 

    var arrKeys1 = StructKeyArray(stc1); 
    var arrKeys2 = StructKeyArray(stc2); 

    ArraySort(arrKeys1, 'text'); 
    ArraySort(arrKeys2, 'text'); 

    if(!ArrayEquals(arrKeys1, arrKeys2, blnCaseSensitiveKeys, blnCaseSensitiveKeys)) 
     return false; 

    for(var i = 1; i <= ArrayLen(arrKeys1); i++) { 
     var strKey = arrKeys1[i]; 

     if(IsStruct(stc1[strKey])) { 
     if(!IsStruct(stc2[strKey])) 
      return false; 
     if(!StructEquals(stc1[strKey], stc2[strKey], blnCaseSensitive, blnCaseSensitiveKeys)) 
      return false; 
     } 
     else if(IsArray(stc1[strKey])) { 
     if(!IsArray(stc2[strKey])) 
      return false; 
     if(!ArrayEquals(stc1[strKey], stc2[strKey], blnCaseSensitive, blnCaseSensitiveKeys)) 
      return false; 
     } 
     else if(IsSimpleValue(stc1[strKey]) && IsSimpleValue(stc2[strKey])) { 
     if(blnCaseSensitive) { 
      if(Compare(stc1[strKey], stc2[strKey]) != 0) 
      return false; 
     } 
     else { 
      if(CompareNoCase(stc1[strKey], stc2[strKey]) != 0) 
      return false; 
     } 
     } 
     else { 
     throw("Can only compare structures, arrays, and simple values. No queries or complex objects."); 
     } 
    } 

    return true; 
    </cfscript> 
</cffunction> 

<cffunction name="ArrayEquals" access="public" returntype="boolean" output="false" 
      hint="Returns whether two arrays are equal, including deep comparison if the arrays contain structures or sub-arrays."> 
    <cfargument name="arr1" type="array" required="true" hint="First struct to be compared." /> 
    <cfargument name="arr2" type="array" required="true" hint="Second struct to be compared." /> 
    <cfargument name="blnCaseSensitive" type="boolean" required="false" default="false" hint="Whether or not values are compared case-sensitive." /> 
    <cfargument name="blnCaseSensitiveKeys" type="boolean" required="false" default="false" hint="Whether or not structure keys are compared case-sensitive, if array contains structures." /> 
    <cfscript> 
    if(ArrayLen(arr1) != ArrayLen(arr2)) 
     return false; 

    for(var i = 1; i <= ArrayLen(arr1); i++) { 
     if(IsStruct(arr1[i])) { 
     if(!IsStruct(arr2[i])) 
      return false; 
     if(!StructEquals(arr1[i], arr2[i], blnCaseSensitive, blnCaseSensitiveKeys)) 
      return false; 
     } 
     else if(IsArray(arr1[i])) { 
     if(!IsArray(arr2[i])) 
      return false; 
     if(!ArrayEquals(arr1[i], arr2[i], blnCaseSensitive, blnCaseSensitiveKeys)) 
      return false; 
     } 
     else if(IsSimpleValue(arr1[i]) && IsSimpleValue(arr2[i])) { 
     if(blnCaseSensitive) { 
      if(Compare(arr1[i], arr2[i]) != 0) 
      return false; 
     } 
     else { 
      if(CompareNoCase(arr1[i], arr2[i]) != 0) 
      return false; 
     } 
     } 
     else { 
     throw("Can only compare structures, arrays, and simple values. No queries or complex objects."); 
     } 
    } 

    return true; 
    </cfscript> 
</cffunction> 

Einheit Tests für alle Interessierten:

public void function test_StructEquals() { 
    AssertTrue(utils.StructEquals({}, StructNew())); 
    AssertTrue(utils.StructEquals({}, StructNew(), true, true)); 

    AssertFalse(utils.StructEquals({}, {"a": "b", "c": "d"})); 

    AssertTrue(utils.StructEquals({"a": "b", "c": "d"}, {"C": "D", "A": "B"})); 
    AssertFalse(utils.StructEquals({"a": "b", "c": "d"}, {"C": "D", "A": "B"}, true, false)); 
    AssertFalse(utils.StructEquals({"a": "b", "c": "d"}, {"C": "D", "A": "B"}, false, true)); 

    AssertTrue(utils.StructEquals({"a": "b", "c": "d"}, {"C": "d", "A": "b"})); 
    AssertTrue(utils.StructEquals({"a": "b", "c": "d"}, {"C": "d", "A": "b"}, true, false)); 
    AssertFalse(utils.StructEquals({"a": "b", "c": "d"}, {"C": "d", "A": "b"}, false, true)); 

    AssertTrue(utils.StructEquals({"a": "b", "c": "d"}, {"c": "D", "a": "B"})); 
    AssertFalse(utils.StructEquals({"a": "b", "c": "d"}, {"c": "D", "a": "B"}, true, false)); 
    AssertTrue(utils.StructEquals({"a": "b", "c": "d"}, {"c": "D", "a": "B"}, false, true)); 

    var stc1 = { 
    "test": { 
     "hello": "world", 
     "goodbye": "space", 
     "somearr": [ 
     { "a": 1, "b": 2 }, 
     "WORD", 
     [ 
      { "x": 97, "y": 98, "z": 99 }, 
      { "i": 50, "j": 51, "k": 52 } 
     ] 
     ] 
    } 
    }; 
    var stc2 = { 
    "test": { 
     "goodbye": "space", 
     "hello": "world", 
     "somearr": [ 
     { "a": 1, "b": 2 }, 
     "WORD", 
     [ 
      { "z": 99, "x": 97, "y": 98 }, 
      { "i": 50, "k": 52, "j": 51 } 
     ] 
     ] 
    } 
    }; 

    AssertTrue(utils.StructEquals(stc1, stc2, true, true)); 
    stc2.test.somearr[2] = "WOrD"; 
    AssertTrue(utils.StructEquals(stc1, stc2)); 
    AssertTrue(utils.StructEquals(stc1, stc2, false, true)); 
    AssertFalse(utils.StructEquals(stc1, stc2, true, false)); 
    stc2.test.somearr[3][1] = { "z": 99, "X": 97, "y": 98 }; 
    AssertTrue(utils.StructEquals(stc1, stc2)); 
    AssertFalse(utils.StructEquals(stc1, stc2, false, true)); 
    AssertFalse(utils.StructEquals(stc1, stc2, true, false)); 
    stc2.test.somearr[2] = "WORD"; 
    AssertTrue(utils.StructEquals(stc1, stc2)); 
    AssertFalse(utils.StructEquals(stc1, stc2, false, true)); 
    AssertTrue(utils.StructEquals(stc1, stc2, true, false)); 
} 

public void function test_ArrayEquals() { 
    AssertTrue(utils.ArrayEquals([], ArrayNew(1))); 
    AssertTrue(utils.ArrayEquals([], ArrayNew(1), true, true)); 

    AssertFalse(utils.ArrayEquals([], [1, 2, 3])); 

    AssertTrue(utils.ArrayEquals(['a', 'b', 'c'], ['A', 'B', 'C'])); 
    AssertFalse(utils.ArrayEquals(['a', 'b', 'c'], ['A', 'B', 'C'], true, false)); 
    AssertTrue(utils.ArrayEquals(['a', 'b', 'c'], ['A', 'B', 'C'], false, true)); 

    AssertFalse(utils.ArrayEquals(['a', 'b', 'c'], ['a', 'c', 'b'])); 

    AssertTrue(utils.ArrayEquals([1, 2, 3], [1, 2, 3])); 
    AssertFalse(utils.ArrayEquals([1, 2, 3], [1, 3, 2])); 
}