@@ -1072,6 +1072,121 @@ def compare_func(x, y, level=None):
1072
1072
assert expected_with_compare_func == ddiff2
1073
1073
assert ddiff != ddiff2
1074
1074
1075
+ def test_ignore_order_with_compare_func_with_one_each_hashes_added_hashes_removed (self ):
1076
+ """
1077
+ Scenario:
1078
+ In this example which demonstrates the problem... We have two dictionaries containing lists for
1079
+ individualNames. Each list contains exactly 2 elements. The effective change is that we are
1080
+ replacing the 2nd element in the list.
1081
+ NOTE: This is considered a REPLACEMENT of the second element and not an UPDATE of the element
1082
+ because we are providing a custom compare_func which will determine matching elements based on
1083
+ the value of the nameIdentifier field. If the custom compare_func is not used, then
1084
+ deepdiff.diff will mistakenly treat the difference as being individual field updates for every
1085
+ field in the second element of the list.
1086
+
1087
+ Intent:
1088
+ Use our custom compare_func, since we have provided it.
1089
+ We need to fall into self._precalculate_distance_by_custom_compare_func
1090
+ To do this, we are proposing a change to deepdiff.diff line 1128:
1091
+
1092
+ Original:
1093
+ if hashes_added and hashes_removed and self.iterable_compare_func and len(hashes_added) > 1 and len(hashes_removed) > 1:
1094
+
1095
+ Proposed/Updated:
1096
+ if hashes_added and hashes_removed \
1097
+ and self.iterable_compare_func \
1098
+ and len(hashes_added) > 0 and len(hashes_removed) > 0:
1099
+
1100
+ NOTE: It is worth mentioning that deepdiff.diff line 1121, might also benefit by changing the length conditions
1101
+ to evaluate for > 0 (rather than > 1).
1102
+ """
1103
+
1104
+ t1 = {
1105
+ "individualNames" : [
1106
+ {
1107
+ "firstName" : "Johnathan" ,
1108
+ "lastName" : "Doe" ,
1109
+ "prefix" : "COLONEL" ,
1110
+ "middleName" : "A" ,
1111
+ "primaryIndicator" : True ,
1112
+ "professionalDesignation" : "PHD" ,
1113
+ "suffix" : "SR" ,
1114
+ "nameIdentifier" : "00001"
1115
+ },
1116
+ {
1117
+ "firstName" : "John" ,
1118
+ "lastName" : "Doe" ,
1119
+ "prefix" : "" ,
1120
+ "middleName" : "" ,
1121
+ "primaryIndicator" : False ,
1122
+ "professionalDesignation" : "" ,
1123
+ "suffix" : "SR" ,
1124
+ "nameIdentifier" : "00002"
1125
+ }
1126
+ ]
1127
+ }
1128
+
1129
+ t2 = {
1130
+ "individualNames" : [
1131
+ {
1132
+ "firstName" : "Johnathan" ,
1133
+ "lastName" : "Doe" ,
1134
+ "prefix" : "COLONEL" ,
1135
+ "middleName" : "A" ,
1136
+ "primaryIndicator" : True ,
1137
+ "professionalDesignation" : "PHD" ,
1138
+ "suffix" : "SR" ,
1139
+ "nameIdentifier" : "00001"
1140
+ },
1141
+ {
1142
+ "firstName" : "Johnny" ,
1143
+ "lastName" : "Doe" ,
1144
+ "prefix" : "" ,
1145
+ "middleName" : "A" ,
1146
+ "primaryIndicator" : False ,
1147
+ "professionalDesignation" : "" ,
1148
+ "suffix" : "SR" ,
1149
+ "nameIdentifier" : "00003"
1150
+ }
1151
+ ]
1152
+ }
1153
+ def compare_func (item1 , item2 , level = None ):
1154
+ print ("*** inside compare ***" )
1155
+ it1_keys = item1 .keys ()
1156
+
1157
+ try :
1158
+
1159
+ # --- individualNames ---
1160
+ if 'nameIdentifier' in it1_keys and 'lastName' in it1_keys :
1161
+ match_result = item1 ['nameIdentifier' ] == item2 ['nameIdentifier' ]
1162
+ print ("individualNames - matching result:" , match_result )
1163
+ return match_result
1164
+ else :
1165
+ print ("Unknown list item..." , "matching result:" , item1 == item2 )
1166
+ return item1 == item2
1167
+ except Exception :
1168
+ raise CannotCompare () from None
1169
+ # ---------------------------- End of nested function
1170
+
1171
+ actual_diff = DeepDiff (t1 , t2 , report_repetition = True ,
1172
+ ignore_order = True , iterable_compare_func = compare_func , cutoff_intersection_for_pairs = 1 )
1173
+
1174
+ old_invalid_diff = {
1175
+ 'values_changed' : {"root['individualNames'][1]['firstName']" : {'new_value' : 'Johnny' , 'old_value' : 'John' },
1176
+ "root['individualNames'][1]['middleName']" : {'new_value' : 'A' , 'old_value' : '' },
1177
+ "root['individualNames'][1]['nameIdentifier']" : {'new_value' : '00003' ,
1178
+ 'old_value' : '00002' }}}
1179
+ new_expected_diff = {'iterable_item_added' : {
1180
+ "root['individualNames'][1]" : {'firstName' : 'Johnny' , 'lastName' : 'Doe' , 'prefix' : '' , 'middleName' : 'A' ,
1181
+ 'primaryIndicator' : False , 'professionalDesignation' : '' , 'suffix' : 'SR' ,
1182
+ 'nameIdentifier' : '00003' }}, 'iterable_item_removed' : {
1183
+ "root['individualNames'][1]" : {'firstName' : 'John' , 'lastName' : 'Doe' , 'prefix' : '' , 'middleName' : '' ,
1184
+ 'primaryIndicator' : False , 'professionalDesignation' : '' , 'suffix' : 'SR' ,
1185
+ 'nameIdentifier' : '00002' }}}
1186
+
1187
+ assert old_invalid_diff != actual_diff
1188
+ assert new_expected_diff == actual_diff
1189
+
1075
1190
1076
1191
class TestDynamicIgnoreOrder :
1077
1192
def test_ignore_order_func (self ):
0 commit comments