2010-06-30 16 views
8

(Esta es una pregunta de coldfusion)¿Cómo puedo hacer una "comparación profunda" o "diff" en dos Structs?

Tengo dos estructuras diferentes que pueden contener o no los mismos datos, y quiero poder ver si lo hacen. Mis Estructuras siempre contendrán valores simples (Números, Cadenas o Booleanos) porque se están creando con DeserializeJSON, así que con suerte esto se puede hacer fácilmente.

Encontré la publicación de Ben Nadel here, pero esa técnica no parece funcionar para mí. Esto es lo que he probado hasta ahora (un código cfwheels allí):

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

Y los resultados de ese aspecto:

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 

así, como se verá más arriba, aunque el los datos dentro de los Structs parecen coincidir exactamente con que no pasan la prueba Equals().

¿Alguien más ha hecho esto con éxito?

+0

hmm .. typo porque "ID número 70634" y "idnumber 70634" no son lo mismo – Henry

+0

sí, eso sí fue un error tipográfico. –

Respuesta

9

Aquí está la solución de Ben adaptado rápidamente a mis necesidades, se puede ajustar aún más (y es de esperar que sea 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

gracias por la respuesta rápida, ¡esto está muy cerca de lo que necesito! –

+0

Creo que quiere 'NOT arguments.ignoreFirstEmptyString' en las líneas 25 y' NOT arguments.ignoreSecondEmptyString' en la línea 37. TAL CUAL si existe una clave en una estructura pero no en la otra, no se devolverá. Si usa 'NOT', devolverá la clave si está en una estructura y no en la otra. – kralco626

4

Si está utilizando CF9 o Railo 3

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

o

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

excelente información. aunque estaba tratando de hacer una simple comparación de verdadero o falso en mi pregunta, la matriz de diferencias generada por la función de zarko ha inspirado un cambio en mi interfaz que ofrecerá una funcionalidad mucho más útil.Sin embargo, podría usar uno de estos para hacer una comprobación inicial antes de ejecutar el diff completo. –

3

Oculto en Coldfusion Structures es un pequeño método práctico llamado hashCode(). Aunque tenga en cuenta que esto no está documentado.

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

</cfif> 
+0

¡interesante! ¿Sabes si esto también funciona en Railo? –

+1

No, simplemente lo probé en Railo y el valor de hashCode siempre es diferente. Aunque ... st1.toString() Eq st2.toString() funciona en Railo;) –

1

También puede realizar esto utilizando el método nativo de Java heredado por el CFC.

isThisTrue = ObjA.equals(ObjB); 
+2

tenga en cuenta que esta es la técnica que demostré en la publicación inicial. con mis dos estructuras que parecen similares, arroja "falso". equals() no funciona en todos los casos. –

0

He aquí algo que lancé rápidamente. Tiene un parámetro para determinar si hacer o no una comparación de valores y claves sensible a mayúsculas y minúsculas. Tire estas dos funciones (StructEquals(), ArrayEquals()) en algún tipo de utilidades CFC.

Limitación: No funciona para estructuras/matrices que contienen consultas u objetos.

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

Unidad de Pruebas de para cualquier persona interesada:

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