-
Notifications
You must be signed in to change notification settings - Fork 20
Expand file tree
/
Copy pathmanager.py
More file actions
70 lines (57 loc) · 2.29 KB
/
manager.py
File metadata and controls
70 lines (57 loc) · 2.29 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
from typing import List
from collections import defaultdict
from nodes.node import Node
class NodeManager:
"""
Manages a collection of Node objects, allowing single or cascade removal
while respecting hierarchical dependencies.
"""
nodes: List[Node]
def __init__(self, nodes: List[Node]) -> None:
"""
Initialize NodeManager with a list of Node instances.
Raises:
ValueError: If input is not a list of Node objects.
"""
if not isinstance(nodes, list) or not all(isinstance(n, Node) for n in nodes):
raise ValueError("NodeManager must be initialized with a list of Node objects.")
self.nodes = list(nodes) # Create a shallow copy to avoid mutating input
def __len__(self) -> int:
"""Return the number of nodes managed."""
return len(self.nodes)
def __getitem__(self, index: int) -> Node:
"""Allow indexed access to nodes (manager[index])."""
return self.nodes[index]
def remove(self, node: Node) -> None:
"""
Remove the specified node only.
Raises:
ValueError: If the node does not exist in the manager.
"""
try:
self.nodes.remove(node)
except ValueError:
raise ValueError("Node not found in manager.")
def remove_cascade(self, node: Node) -> None:
"""
Remove the specified node and all its descendants recursively.
Raises:
ValueError: If the node does not exist in the manager.
"""
if node not in self.nodes:
raise ValueError("Node not found in manager.")
# Build parent_id -> list of children mapping for fast lookup
parent_map = defaultdict(list)
for n in self.nodes:
parent_map[n.parent].append(n)
# Depth-First Search (DFS) to collect all descendant nodes
to_remove_ids = set()
stack = [node.id]
while stack:
current_id = stack.pop()
to_remove_ids.add(current_id)
for child in parent_map.get(current_id, []):
if child.id not in to_remove_ids:
stack.append(child.id)
# Filter out all nodes that should be removed
self.nodes = [n for n in self.nodes if n.id not in to_remove_ids]