Come posso fare una “profonda confrontare” o “diff” su due Structs?
-
01-10-2019 - |
Domanda
(Questa è una domanda ColdFusion)
Ho due Structs differenti che possono o non possono contenere gli stessi dati, e voglio essere in grado di vedere se lo fanno! I miei Structs conterrà sempre valori semplici (numeri, stringhe o booleane) perché sono stati creati con DeserializeJSON, quindi spero che questo può essere fatto facilmente.
ho trovato il post di Ben Nadel qui , ma che la tecnica non sembra lavorare per me. Ecco quello che ho provato finora (codice cfwheels in là):
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);
E i risultati di quello sguardo, come:
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
in modo, come si vedrà in precedenza, anche se i dati all'interno dei Structs sembrano corrispondere esattamente non passano le Equals () di prova.
Qualcun altro ha fatto questo successo?
Soluzione
Ecco la soluzione di Ben adattato rapidamente alle mie esigenze, è possibile regolare ulteriormente (e, si spera rendere 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>
Altri suggerimenti
Se stai usando 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
Nascosto in strutture Coldfusion è un pratico metodo di poco chiamato hashCode (). Anche se tenere a mente che questo è documentato.
<cfif struct1.hashCode() Eq struct2.hashCode()>
</cfif>
È inoltre possibile eseguire questa operazione utilizzando il metodo Java nativo ereditato dal CFC.
isThisTrue = ObjA.equals(ObjB);
Ecco cosa ho buttato insieme in fretta. Ha parametro per determinare se o non fare il confronto-sensitive di valori e chiavi. Gettare queste due funzioni (StructEquals()
, ArrayEquals()
) in una sorta di utilità CFC.
Limitazione :. Non funziona per struct / array contenente domande o oggetti
<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>
Unità Test per chiunque sia interessato:
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]));
}