8
votes

Comment puis-je faire un "comparateur profond" ou "diff" sur deux structures?

(Ceci est une question de coldfusion)

J'ai deux structures différentes pouvant contenir ou non les mêmes données, et je veux pouvoir voir s'ils le font! Mes structures contiendront toujours des valeurs simples (chiffres, cordes ou booléens) car ils sont créés avec DESERIALIZONJSON, alors j'espère que cela peut être fait facilement.

J'ai trouvé le message de Ben Nadel ici , mais cette technique ne semble pas fonctionner pour moi. Voici ce que j'ai essayé jusqu'à présent (un code CFWheels de là): xxx

et les résultats à qui ressemble: xxx

Donc, comme vous le verrez ci-dessus, bien que les données à l'intérieur des structures semblent correspondre exactement, elles ne transmettent pas le test Equals ().

Quelqu'un d'autre a-t-il fait avec succès?


1 commentaires

hmm .. Typo? "Cause" Numéro d'identification 70634 "et" IDNumber 70634 "ne sont pas les mêmes


6 Réponses :


9
votes

Voici la solution de Ben est rapidement ajustée à mes besoins, vous pouvez l'ajuster plus loin (et espérons-y le faire préserver):

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


2 commentaires

Merci pour la réponse rapide - c'est très proche de ce dont j'ai besoin!


Je pense que vous voulez non arguments.ignorefirStyring sur les lignes 25 et non arguments.ignoreecondemptyString à la ligne 37. Comme si une clé existe dans une structure mais non dans l'autre, elle sera ne pas être retourné. Si vous utilisez le pas , il retournera la clé si elle est dans une structure et non l'autre.



4
votes

Si vous utilisez cf9 ou Railo 3

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


1 commentaires

super info. Bien que j'essayais de faire un simple vrai ou faux comparer dans ma question, la matrice DIFF générée par la fonction de Zarko a inspiré une modification de mon interface qui offrira une fonctionnalité beaucoup plus utile. Je pourrais utiliser l'un d'entre eux pour effectuer une vérification initiale avant d'exécuter la diffusion complète, cependant.



3
votes

caché dans les structures de coldfusion est une petite méthode pratique appelée hashcode (). Bien que ce soit à l'esprit que cela est sans papiers. XXX


1 commentaires

Nope, je viens de l'essayer sur Railo et la valeur Hashcode est toujours différente. Bien que ... ST1.TOSTRING () EQ ST2.TOSTRING () fonctionne sur RAILO;)



1
votes

Vous pouvez également effectuer cela à l'aide de la méthode Java natif héritée par le CFC.

isThisTrue = ObjA.equals(ObjB);


1 commentaires

Notez que c'est la technique que j'ai démontrée dans le poste initial. Avec mes deux autres structs apparaissant semblables, il donne "faux". Equals () ne fonctionne pas dans tous les cas.



0
votes

Voici quelque chose que j'ai jeté ensemble. Il a un paramètre pour déterminer s'il faut ou non une comparaison sensible à la casse des valeurs et des clés. Jeter ces deux fonctions ( structure () code>, arrayequaux () code>) dans une sorte d'utilitaires cfc.

limitation forte>: ne fonctionne pas pour STRUCTIONS / TAREILS contenant des requêtes ou des objets. P>

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


0 commentaires

0
votes
if(serializeJSON(itemA) eq serializeJSON(itemB))
  //They match!
else
  //They don't!
You are already using JSON to manipulate these, you should just keep with that.
Not that anyone cares a decade later, but that method works for me.

0 commentaires