|
2 | 2 |
|
3 | 3 | import copy |
4 | 4 | import typing as t |
| 5 | +from collections import deque |
5 | 6 | from datetime import datetime, timedelta |
6 | 7 | from decimal import Decimal |
7 | 8 | from enum import Enum |
@@ -107,31 +108,35 @@ def merge( |
107 | 108 | } |
108 | 109 |
|
109 | 110 | @staticmethod |
110 | | - def compact(value: t.Dict[str, t.Any]) -> t.Dict[str, t.Any]: |
111 | | - """Remove all `Undefined` values from a dictionary.""" |
112 | | - queue: t.List[t.Dict[str, t.Any]] = [{"obj": {"o": value}, "prop": "o"}] |
113 | | - refs: t.List[t.Any] = [] |
114 | | - |
115 | | - for i in range(len(queue)): # pylint: disable=C0200 |
116 | | - item: t.Mapping[t.Any, t.Any] = queue[i] |
117 | | - obj: t.Mapping[t.Any, t.Any] = item["obj"][item["prop"]] |
118 | | - |
119 | | - keys: t.List[t.Any] = list(obj.keys()) |
120 | | - for key in keys: |
121 | | - val = obj.get(key) |
122 | | - |
123 | | - if ( |
124 | | - val is not None |
125 | | - and not isinstance(val, Undefined) |
126 | | - and isinstance(val, t.Mapping) |
127 | | - and val not in refs |
128 | | - ): |
129 | | - queue.append({"obj": obj, "prop": key}) |
130 | | - refs.append(val) |
131 | | - |
132 | | - Utils._remove_undefined_from_map(value) |
133 | | - |
134 | | - return value |
| 111 | + def compact(root: t.Dict) -> t.Dict: |
| 112 | + """Remove all instances of Undefined from the object.""" |
| 113 | + stack: deque[t.Union[t.Dict, t.List]] = deque([root]) |
| 114 | + visited: t.Set[int] = {id(root)} |
| 115 | + |
| 116 | + while stack: |
| 117 | + node: t.Union[t.Dict, t.List] = stack.pop() |
| 118 | + if isinstance(node, dict): |
| 119 | + # copy keys to allow deletion during iteration |
| 120 | + for k in list(node.keys()): |
| 121 | + v: object = node[k] |
| 122 | + if isinstance(v, Undefined): # whatever your sentinel class is |
| 123 | + del node[k] |
| 124 | + elif isinstance(v, (dict, list)): |
| 125 | + if id(v) not in visited: |
| 126 | + visited.add(id(v)) |
| 127 | + stack.append(v) |
| 128 | + elif isinstance(node, list): |
| 129 | + i: int = 0 |
| 130 | + while i < len(node): |
| 131 | + v = node[i] |
| 132 | + if isinstance(v, Undefined): |
| 133 | + del node[i] |
| 134 | + else: |
| 135 | + if isinstance(v, (dict, list)) and id(v) not in visited: |
| 136 | + visited.add(id(v)) |
| 137 | + stack.append(v) |
| 138 | + i += 1 |
| 139 | + return root |
135 | 140 |
|
136 | 141 | @staticmethod |
137 | 142 | def _remove_undefined_from_list(value: t.List[t.Any]) -> None: |
|
0 commit comments