2010-06-30 20 views
8

(Jest to pytanie ColdFusion)Jak mogę dokonać "głębokiego porównania" lub "porównania" w dwóch strukturach?

Mam dwóch różnych strukturach, które mogą lub nie mogą zawierać te same dane, a ja chcę być w stanie sprawdzić, czy robią! Moje Struktury będą zawsze zawierać proste wartości (Liczby, Łańcuchy lub Booleany), ponieważ są tworzone z DeserializeJSON, więc mam nadzieję, że można to łatwo zrobić.

Znalazłem stanowisko Ben Nadel here, ale ta technika nie działa dla mnie. Oto, co już próbowałem tak daleko (niektóre kod cfwheels tam):

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); 

a wyniki, które wyglądają tak:

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 

tak, jak zobaczysz powyżej, chociaż dane wewnątrz Struktur wydają się dokładnie pasować, nie przechodzą testu Equals().

Czy ktoś inny zrobił to pomyślnie?

+0

hmm .. literówka? "przyczyna" nr id 70634 "i" idnumber 70634 "to nie to samo – Henry

+0

tak, to był rzeczywiście literówka! –

Odpowiedz

9

Oto rozwiązanie Bena szybko dostosowane do moich potrzeb, można go dostosować dalej (i miejmy nadzieję uczynić go pretier):

<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

dzięki za szybką odpowiedź - jest to bardzo blisko tego, czego potrzebuję! –

+0

Myślę, że chcesz 'NOT arguments.ignoreFirstEmptyString' na liniach 25 i' NOT arguments.ignoreSecondEmptyString' na linii 37. AS IS, jeśli klucz istnieje w jednej strukturze, ale nie w drugiej nie zostanie zwrócony. Jeśli użyjesz 'NOT', to zwróci klucz, jeśli jest w jednej strukturze, a nie w drugiej. – kralco626

4

Jeśli używasz CF9 lub Railo 3

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

lub

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

świetne informacje. chociaż starałem się wykonać proste, prawdziwe lub fałszywe porównanie w moim pytaniu, tablica diff generowana przez funkcję zarko zainspirowała zmianę w moim interfejsie, która zaoferuje znacznie bardziej użyteczną funkcjonalność.Mogę jednak użyć jednego z nich do wykonania wstępnego sprawdzenia przed uruchomieniem pełnej wersji diff. –

3

Ukryty w Coldfusion Structures jest poręczną małą metodą o nazwie hashCode(). Chociaż należy pamiętać, że jest to nieudokumentowane.

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

</cfif> 
+0

interesujące! czy wiesz, czy to też działa w Railo? –

+1

Nie, po prostu wypróbowałem to na Railo, a wartość hashCode jest zawsze inna. Chociaż ... st1.toString() Eq st2.toString() działa na Railo;) –

1

Możesz również wykonać to przy użyciu natywnej metody Java dziedziczonej przez CFC.

isThisTrue = ObjA.equals(ObjB); 
+2

zauważ, że jest to technika, którą zademonstrowałem w pierwszym poście. z moimi dwiema strukturami, które wyglądają podobnie, daje "false". equals() nie działa we wszystkich przypadkach. –

0

Oto coś, co rzuciłem szybko. Ma parametr określający, czy porównywać wartości i klucze, czy też nie. Wrzuć te dwie funkcje (StructEquals(), ArrayEquals()) do pewnego rodzaju narzędzi CFC.

Ograniczenie: Nie działa dla structs/arrays zawierających zapytania lub obiekty.

<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> 

Jednostka Testy dla wszystkich zainteresowanych:

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])); 
}