Skip to content

Commit f40ed9b

Browse files
authored
Merge pull request #543 from surefyresystems/fill_empty_lists
Allow for filling an array when using force to extend with a known value
2 parents 69adaf1 + 15883dd commit f40ed9b

File tree

3 files changed

+56
-1
lines changed

3 files changed

+56
-1
lines changed

deepdiff/delta.py

+10-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import copy
22
import logging
3-
from typing import List, Dict, IO, Callable, Set, Union, Optional
3+
from typing import List, Dict, IO, Callable, Set, Union, Optional, Any
44
from functools import partial, cmp_to_key
55
from collections.abc import Mapping
66
from copy import deepcopy
@@ -86,6 +86,7 @@ def __init__(
8686
always_include_values: bool=False,
8787
iterable_compare_func_was_used: Optional[bool]=None,
8888
force: bool=False,
89+
fill: Any=not_found,
8990
):
9091
# for pickle deserializer:
9192
if hasattr(deserializer, '__code__') and 'safe_to_import' in set(deserializer.__code__.co_varnames):
@@ -158,6 +159,7 @@ def _deserializer(obj, safe_to_import=None):
158159
self.serializer = serializer
159160
self.deserializer = deserializer
160161
self.force = force
162+
self.fill = fill
161163
if force:
162164
self.get_nested_obj = _get_nested_obj_and_force
163165
else:
@@ -286,6 +288,13 @@ def _simple_set_elem_value(self, obj, path_for_err_reporting, elem=None, value=N
286288
except IndexError:
287289
if elem == len(obj):
288290
obj.append(value)
291+
elif self.fill is not not_found and elem > len(obj):
292+
while len(obj) < elem:
293+
if callable(self.fill):
294+
obj.append(self.fill(obj, value, path_for_err_reporting))
295+
else:
296+
obj.append(self.fill)
297+
obj.append(value)
289298
else:
290299
self._raise_or_log(ELEM_NOT_FOUND_TO_ADD_MSG.format(elem, path_for_err_reporting))
291300
elif action == GETATTR:

docs/delta.rst

+4
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,10 @@ force : Boolean, default=False
6868
always_include_values : Boolean, default=False
6969
:ref:`always_include_values_label` is used to make sure the delta objects includes the values that were changed. Sometime Delta tries to be efficient not include the values when it can get away with it. By setting this parameter to True, you ensure that the Delta object will include the values.
7070

71+
fill : Any, default=No Fill
72+
:ref:`delta_fill` This is only relevant if `force` is set. This parameter only applies when force is set and trying to fill an existing array. If the index of the array being applied is larger than the length of the array this value will be used to fill empty spaces of the array to extend it in order to add the new value. If this parameter is not set, the items will get dropped and the array not extended. If this parameter is set with a callable function, it will get called each time a fill item is needed. It will be provided with three arguments: first argument is the array being filled, second argument is the value that is being added to the array, the third argument is the path that is being added.
73+
Example function: `def fill(obj, value, path): return "Camry" if "car" in path else None`
74+
7175

7276
**Returns**
7377

tests/test_delta.py

+42
Original file line numberDiff line numberDiff line change
@@ -2131,6 +2131,48 @@ def test_delta_force1(self):
21312131
expected = {'x': {'y': {3: 4}}, 'q': {'t': 0.5}}
21322132
assert expected == result
21332133

2134+
def test_delta_force_fill(self):
2135+
t1 = {
2136+
'x': {
2137+
'y': [{"b": "c"}, {"b": "c"}, {"b": "c"}, {"b": "c"}]
2138+
},
2139+
'q': {
2140+
'r': 'abc',
2141+
}
2142+
}
2143+
2144+
t2 = {
2145+
'x': {
2146+
'y': [{"b": "c"}, {"b": "c"}, {"b": "c"}, {"b": "c"}, {"b": "c"}, {"b": "c"}, {"b": "c"}]
2147+
},
2148+
'q': {
2149+
'r': 'abc',
2150+
't': 0.5,
2151+
}
2152+
}
2153+
2154+
diff = DeepDiff(t1, t2)
2155+
2156+
delta = Delta(diff=diff, force=True)
2157+
result = {"x": {"y": [1,]}} + delta
2158+
expected = {'x': {'y': [1]}, 'q': {'t': 0.5}}
2159+
assert expected == result
2160+
2161+
2162+
delta = Delta(diff=diff, force=True, fill=None)
2163+
result = {"x": {"y": [1,]}} + delta
2164+
expected = {'x': {'y': [1, None, None, None, {"b": "c"}, {"b": "c"}, {"b": "c"}]}, 'q': {'t': 0.5}}
2165+
assert expected == result
2166+
2167+
2168+
def fill_func(obj, value, path):
2169+
return value.copy()
2170+
2171+
delta = Delta(diff=diff, force=True, fill=fill_func)
2172+
result = {"x": {"y": [1,]}} + delta
2173+
expected = {'x': {'y': [1, {"b": "c"}, {"b": "c"}, {"b": "c"}, {"b": "c"}, {"b": "c"}, {"b": "c"}]}, 'q': {'t': 0.5}}
2174+
assert expected == result
2175+
21342176
def test_flatten_dict_with_one_key_added(self):
21352177
t1 = {"field1": {"joe": "Joe"}}
21362178
t2 = {"field1": {"joe": "Joe Nobody"}, "field2": {"jimmy": "Jimmy"}}

0 commit comments

Comments
 (0)