Skip to content

Commit

Permalink
Fix for diffing using iterable_compare_func with nested objects.
Browse files Browse the repository at this point in the history
This commit addresses two issues. First ensuring that the diff indexes
for moved items are always relative to t2 (except for removed)
to stay consistent with the rest of the diff types.

Second, when replaying moved items ensure that the new values is
replaced after adding the items. Since the moved items already have any
nested items inside of them, there is no need to replay those nested
added items (it was causing items to get double added).
  • Loading branch information
dustingtorres committed Aug 4, 2022
1 parent 81341e2 commit a3c0684
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 9 deletions.
10 changes: 9 additions & 1 deletion deepdiff/delta.py
Original file line number Diff line number Diff line change
Expand Up @@ -263,13 +263,21 @@ def _del_elem(self, parent, parent_to_obj_elem, parent_to_obj_action,
def _do_iterable_item_added(self):
iterable_item_added = self.diff.get('iterable_item_added', {})
iterable_item_moved = self.diff.get('iterable_item_moved')

# First we need to create a placeholder for moved items.
# This will then get replaced below after we go through added items.
# Without this items can get double added because moved store the new_value and does not need item_added replayed
if iterable_item_moved:
added_dict = {v["new_path"]: v["value"] for k, v in iterable_item_moved.items()}
added_dict = {v["new_path"]: None for k, v in iterable_item_moved.items()}
iterable_item_added.update(added_dict)

if iterable_item_added:
self._do_item_added(iterable_item_added, insert=True)

if iterable_item_moved:
added_dict = {v["new_path"]: v["value"] for k, v in iterable_item_moved.items()}
self._do_item_added(added_dict, insert=False)

def _do_dictionary_item_added(self):
dictionary_item_added = self.diff.get('dictionary_item_added')
if dictionary_item_added:
Expand Down
2 changes: 1 addition & 1 deletion deepdiff/diff.py
Original file line number Diff line number Diff line change
Expand Up @@ -709,7 +709,7 @@ def _diff_iterable_in_order(self, level, parents_ids=frozenset(), _original_type
x,
y,
child_relationship_class=child_relationship_class,
child_relationship_param=i)
child_relationship_param=j)
self._diff(next_level, parents_ids_added)

def _diff_str(self, level):
Expand Down
10 changes: 5 additions & 5 deletions tests/fixtures/compare_func_result1.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@
"root['Cars'][3]['production']"
],
"values_changed": {
"root['Cars'][0]['dealers'][1]['quantity']": {
"root['Cars'][2]['dealers'][0]['quantity']": {
"new_value": 50,
"old_value": 20
},
"root['Cars'][2]['model_numbers'][2]": {
"root['Cars'][1]['model_numbers'][2]": {
"new_value": 3,
"old_value": 4
},
Expand All @@ -20,20 +20,20 @@
}
},
"iterable_item_added": {
"root['Cars'][0]['dealers'][1]": {
"root['Cars'][2]['dealers'][1]": {
"id": 200,
"address": "200 Fake St",
"quantity": 10
},
"root['Cars'][2]['model_numbers'][3]": 4,
"root['Cars'][1]['model_numbers'][3]": 4,
"root['Cars'][0]": {
"id": "7",
"make": "Toyota",
"model": "8Runner"
}
},
"iterable_item_removed": {
"root['Cars'][0]['dealers'][0]": {
"root['Cars'][2]['dealers'][0]": {
"id": 103,
"address": "103 Fake St",
"quantity": 50
Expand Down
73 changes: 71 additions & 2 deletions tests/test_delta.py
Original file line number Diff line number Diff line change
Expand Up @@ -1478,7 +1478,7 @@ def test_compare_func_with_duplicates_removed(self):
t2 = [{'id': 3, 'val': 3}, {'id': 2, 'val': 2}, {'id': 1, 'val': 3}]
ddiff = DeepDiff(t1, t2, iterable_compare_func=self.compare_func, verbose_level=2)
expected = {
'values_changed': {"root[0]['val']": {'new_value': 3, 'old_value': 1}},
'values_changed': {"root[2]['val']": {'new_value': 3, 'old_value': 1}},
'iterable_item_removed': {'root[2]': {'id': 1, 'val': 3}},
'iterable_item_moved': {
'root[0]': {'new_path': 'root[2]', 'value': {'id': 1, 'val': 3}},
Expand All @@ -1495,7 +1495,7 @@ def test_compare_func_with_duplicates_added(self):
t2 = [{'id': 1, 'val': 1}, {'id': 2, 'val': 2}, {'id': 1, 'val': 3}, {'id': 3, 'val': 3}]
ddiff = DeepDiff(t1, t2, iterable_compare_func=self.compare_func, verbose_level=2)
expected = {
'values_changed': {"root[2]['val']": {'new_value': 1, 'old_value': 3}},
'values_changed': {"root[0]['val']": {'new_value': 1, 'old_value': 3}},
'iterable_item_added': {'root[2]': {'id': 1, 'val': 3}},
'iterable_item_moved': {
'root[2]': {'new_path': 'root[0]', 'value': {'id': 1, 'val': 1}},
Expand Down Expand Up @@ -1526,3 +1526,72 @@ def test_compare_func_path_specific(self):
delta = Delta(ddiff)
recreated_t2 = t1 + delta
assert t2 == recreated_t2

def test_compare_func_nested_changes(self):
t1 = {
"TestTable": [
{
"id": "022fb580-800e-11ea-a361-39b3dada34b5",
"name": "Max",
"NestedTable": [
{
"id": "022fb580-800e-11ea-a361-39b3dada34a6",
"NestedField": "Test Field"
}
]
},
{
"id": "022fb580-800e-11ea-a361-12354656532",
"name": "Bob",
"NestedTable": [
{
"id": "022fb580-800e-11ea-a361-39b3dada34c7",
"NestedField": "Test Field 2"
},
]
},
]
}
t2 = {"TestTable": [
{
"id": "022fb580-800e-11ea-a361-12354656532",
"name": "Bob (Changed Name)",
"NestedTable": [
{
"id": "022fb580-800e-11ea-a361-39b3dada34c7",
"NestedField": "Test Field 2 (Changed Nested Field)"
},
{
"id": "new id",
"NestedField": "Test Field 3"
},
{
"id": "newer id",
"NestedField": "Test Field 4"
},
]
},
{
"id": "adding_some_random_id",
"name": "New Name",
"NestedTable": [
{
"id": "random_nested_id_added",
"NestedField": "New Nested Field"
},
{
"id": "random_nested_id_added2",
"NestedField": "New Nested Field2"
},
{
"id": "random_nested_id_added3",
"NestedField": "New Nested Field43"
},
]
}
]}

ddiff = DeepDiff(t1, t2, iterable_compare_func=self.compare_func, verbose_level=2)
delta = Delta(ddiff)
recreated_t2 = t1 + delta
assert t2 == recreated_t2

0 comments on commit a3c0684

Please sign in to comment.