كيف يمكنني القيام "بمقارنة عميق" أو "فرق" على بنيتين؟

StackOverflow https://stackoverflow.com/questions/3147124

سؤال

(هذا سؤال بارد)

لديّ هيكلان مختلفان قد يحتويان أو لا يحتويان على نفس البيانات ، وأريد أن أكون قادرًا على معرفة ما إذا كانا يفعلان! ستحتوي بنيتي دائمًا على قيم بسيطة (الأرقام أو الأوتار أو المنطقية) لأنها يتم إنشاؤها باستخدام DeserializeJson ، لذلك نأمل أن يتم ذلك بسهولة.

لقد وجدت منشور بن نادل هنا, ، لكن هذه التقنية لا يبدو أنها تعمل من أجلي. إليكم ما جربته حتى الآن (بعض كود CFWHEELS هناك):

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

ونتائج ذلك تبدو:

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

لذلك ، كما سترى أعلاه ، على الرغم من أن البيانات الموجودة داخل الهياكل تتطابق تمامًا مع أنها لا تجتاز اختبار متساو ().

هل قام أي شخص آخر بهذا بنجاح؟

هل كانت مفيدة؟

المحلول

إليك حل Ben الذي تم ضبطه بسرعة على احتياجاتي ، يمكنك ضبطه أكثر (ونأمل أن نجعله ذكيًا):

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

نصائح أخرى

إذا كنت تستخدم CF9 أو Railo 3

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

أو

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

مخبأة في هياكل البارد هي طريقة صغيرة مفيدة تسمى hashcode (). على الرغم من أن ضع في اعتبارك أن هذا غير موثق.

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

</cfif>

يمكنك أيضًا تنفيذ هذا باستخدام طريقة Java الأصلية الموروثة بواسطة CFC.

isThisTrue = ObjA.equals(ObjB);

هذا شيء رميته معًا بسرعة. يحتوي على معلمة لتحديد ما إذا كان يجب إجراء مقارنة حساسة للقيم والمفاتيح. رمي هاتين الوظيفتين (StructEquals(), ArrayEquals()) في نوع من المرافق CFC.

قيود: لا يعمل للهياكل/المصفوفات التي تحتوي على استفسارات أو كائنات.

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

اختبارات الوحدة لأي شخص مهتم:

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]));
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top