0
# Data Manipulation
1
2
Operations for modifying dictionary structures including selective deletion and sophisticated deep merging with configurable behavior for different data integration scenarios.
3
4
## Capabilities
5
6
### Deletion Operations
7
8
Remove elements from nested dictionaries using glob patterns with support for filtering and preservation of data structure integrity.
9
10
```python { .api }
11
def delete(obj, glob, separator="/", afilter=None):
12
"""
13
Delete all elements matching glob pattern.
14
15
Parameters:
16
- obj (MutableMapping): Target dictionary to modify
17
- glob (Glob): Path pattern as string or sequence
18
- separator (str): Path separator character (default "/")
19
- afilter (Filter): Optional function to filter which elements to delete
20
21
Returns:
22
int: Number of elements deleted
23
24
Raises:
25
- PathNotFound: If no matching paths found to delete
26
"""
27
```
28
29
#### Usage Examples
30
31
```python
32
import dpath
33
from dpath.exceptions import PathNotFound
34
35
data = {
36
"users": {
37
"john": {"age": 30, "active": True, "temp_data": "delete_me"},
38
"jane": {"age": 25, "active": False, "temp_data": "also_delete"},
39
"bob": {"age": 35, "active": True}
40
},
41
"temp_settings": {"cache": True},
42
"permanent_settings": {"theme": "dark"}
43
}
44
45
# Delete exact path
46
count = dpath.delete(data, "users/john/temp_data") # Returns: 1
47
# Removes temp_data from john's record
48
49
# Delete with wildcards - removes matching fields from all users
50
count = dpath.delete(data, "users/*/temp_data") # Returns: 2 (john and jane)
51
52
# Delete entire user records
53
count = dpath.delete(data, "users/jane") # Returns: 1
54
# Removes jane's entire record
55
56
# Delete with filter - only delete inactive users
57
def inactive_filter(user):
58
return isinstance(user, dict) and not user.get("active", True)
59
60
count = dpath.delete(data, "users/*", afilter=inactive_filter)
61
# Only deletes users where active=False
62
63
# Delete all temporary data across entire structure
64
count = dpath.delete(data, "**/temp_*") # Matches any key starting with "temp_"
65
66
# Handle deletion errors
67
try:
68
dpath.delete(data, "nonexistent/path")
69
except PathNotFound as e:
70
print(f"Nothing to delete: {e}")
71
72
# Delete list elements
73
list_data = {"items": ["a", "b", "c", "d"]}
74
count = dpath.delete(list_data, "items/1") # Removes "b"
75
# Note: List behavior depends on position - middle elements become None
76
```
77
78
### Merge Operations
79
80
Deeply merge dictionaries with sophisticated control over how conflicts are resolved and data is combined.
81
82
```python { .api }
83
def merge(dst, src, separator="/", afilter=None, flags=MergeType.ADDITIVE):
84
"""
85
Deep merge source into destination dictionary.
86
87
Parameters:
88
- dst (MutableMapping): Destination dictionary (modified in place)
89
- src (MutableMapping): Source dictionary to merge from
90
- separator (str): Path separator for filtered merging (default "/")
91
- afilter (Filter): Optional function to filter which parts of src to merge
92
- flags (MergeType): Merge behavior flags (default MergeType.ADDITIVE)
93
94
Returns:
95
MutableMapping: The modified destination dictionary
96
97
Note:
98
Creates references, not deep copies. Source objects may be modified
99
by subsequent operations on the destination.
100
"""
101
```
102
103
#### Merge Behavior Flags
104
105
```python { .api }
106
from dpath.types import MergeType
107
108
class MergeType(IntFlag):
109
ADDITIVE = auto() # Combine lists by concatenation (default)
110
REPLACE = auto() # Replace destination lists with source lists
111
TYPESAFE = auto() # Raise TypeError when merging incompatible types
112
```
113
114
#### Usage Examples
115
116
```python
117
import dpath
118
from dpath.types import MergeType
119
120
# Basic merge - combines dictionaries recursively
121
dst = {
122
"users": {"john": {"age": 30}},
123
"settings": {"theme": "light"}
124
}
125
126
src = {
127
"users": {"jane": {"age": 25}, "john": {"city": "NYC"}},
128
"settings": {"lang": "en"}
129
}
130
131
dpath.merge(dst, src)
132
# Result: {
133
# "users": {"john": {"age": 30, "city": "NYC"}, "jane": {"age": 25}},
134
# "settings": {"theme": "light", "lang": "en"}
135
# }
136
137
# List merging with ADDITIVE (default)
138
dst = {"tags": ["python", "data"]}
139
src = {"tags": ["analysis", "ml"]}
140
dpath.merge(dst, src) # tags becomes ["python", "data", "analysis", "ml"]
141
142
# List merging with REPLACE
143
dst = {"tags": ["python", "data"]}
144
src = {"tags": ["analysis", "ml"]}
145
dpath.merge(dst, src, flags=MergeType.REPLACE) # tags becomes ["analysis", "ml"]
146
147
# Type-safe merging
148
dst = {"count": 10}
149
src = {"count": "ten"} # String instead of int
150
151
try:
152
dpath.merge(dst, src, flags=MergeType.TYPESAFE)
153
except TypeError as e:
154
print(f"Type mismatch: {e}")
155
156
# Filtered merging - only merge specific parts
157
def settings_filter(value):
158
# Only merge settings, not user data
159
return True # Apply filter logic here
160
161
filtered_src = dpath.search(src, "settings/**")
162
dpath.merge(dst, filtered_src)
163
164
# Combined flags
165
dpath.merge(dst, src, flags=MergeType.REPLACE | MergeType.TYPESAFE)
166
```
167
168
### Advanced Deletion Patterns
169
170
#### Smart List Deletion
171
172
```python
173
# List deletion preserves order for end elements
174
data = {"items": ["a", "b", "c", "d", "e"]}
175
176
# Deleting last element truly removes it
177
dpath.delete(data, "items/-1") # or "items/4"
178
# Result: ["a", "b", "c", "d"]
179
180
# Deleting middle elements sets to None to preserve indices
181
dpath.delete(data, "items/1")
182
# Result: ["a", None, "c", "d", "e"]
183
184
# Delete multiple list elements
185
for i in reversed(range(1, 4)): # Delete backwards to maintain indices
186
dpath.delete(data, f"items/{i}")
187
```
188
189
#### Conditional Deletion
190
191
```python
192
# Delete based on value properties
193
data = {
194
"products": {
195
"item1": {"price": 10, "discontinued": True},
196
"item2": {"price": 25, "discontinued": False},
197
"item3": {"price": 15, "discontinued": True}
198
}
199
}
200
201
# Delete discontinued products
202
def discontinued_filter(product):
203
return isinstance(product, dict) and product.get("discontinued", False)
204
205
count = dpath.delete(data, "products/*", afilter=discontinued_filter)
206
# Removes item1 and item3
207
208
# Delete based on value ranges
209
def expensive_filter(product):
210
return isinstance(product, dict) and product.get("price", 0) > 20
211
212
count = dpath.delete(data, "products/*", afilter=expensive_filter)
213
```
214
215
### Advanced Merge Scenarios
216
217
#### Merging with Deep Copy Prevention
218
219
```python
220
import copy
221
222
# Problem: Merge creates references
223
dst = {"data": {"list": [1, 2]}}
224
src = {"data": {"list": [3, 4]}}
225
226
dpath.merge(dst, src) # dst["data"]["list"] now references src's list
227
src["data"]["list"].append(5) # This also affects dst!
228
229
# Solution: Deep copy source before merging
230
src_copy = copy.deepcopy(src)
231
dpath.merge(dst, src_copy) # Now changes to src won't affect dst
232
```
233
234
#### Complex Merge Workflows
235
236
```python
237
# Multi-source merging with different behaviors
238
base_config = {"database": {"host": "localhost"}}
239
240
# Merge environment-specific overrides
241
env_config = {"database": {"port": 5432}}
242
dpath.merge(base_config, env_config)
243
244
# Merge user preferences with replacement for UI settings
245
user_config = {"ui": {"theme": "dark", "panels": ["editor", "terminal"]}}
246
dpath.merge(base_config, user_config, flags=MergeType.REPLACE)
247
248
# Merge runtime settings type-safely
249
runtime_config = {"database": {"timeout": 30}}
250
dpath.merge(base_config, runtime_config, flags=MergeType.TYPESAFE)
251
```
252
253
#### Selective Merging
254
255
```python
256
# Merge only specific branches
257
def merge_branch(dst, src, branch_pattern):
258
"""Merge only specific branches of source into destination"""
259
branch_data = dpath.search(src, branch_pattern)
260
dpath.merge(dst, branch_data)
261
262
# Usage
263
merge_branch(dst, src, "settings/**") # Only merge settings branch
264
merge_branch(dst, src, "users/*/profile") # Only merge user profiles
265
```
266
267
## Error Handling and Edge Cases
268
269
```python
270
from dpath.exceptions import PathNotFound, InvalidKeyName
271
272
# Handle empty string keys (requires option)
273
import dpath.options
274
dpath.options.ALLOW_EMPTY_STRING_KEYS = True
275
276
data = {"": "empty key value"} # Now allowed
277
278
# Handle deletion of non-existent paths
279
try:
280
dpath.delete(data, "path/that/does/not/exist")
281
except PathNotFound:
282
print("Nothing to delete")
283
284
# Handle invalid key names
285
try:
286
dpath.new(data, "path/with//empty/segment", "value")
287
except InvalidKeyName as e:
288
print(f"Invalid key: {e}")
289
290
# Merge type conflicts
291
dst = {"value": [1, 2, 3]}
292
src = {"value": {"nested": "dict"}}
293
294
# This will replace the list with dict (default behavior)
295
dpath.merge(dst, src) # dst["value"] becomes {"nested": "dict"}
296
```