Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 68 additions & 3 deletions nodes/manager.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,70 @@
# Create here the NodeManager Class

from typing import List
from collections import defaultdict
from nodes.node import Node

class NodeManager:
pass
"""
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]
50 changes: 47 additions & 3 deletions nodes/node.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,49 @@
# Create here the Node Class

from typing import Any

class Node:
pass
"""
Represents a node in a hierarchical document/tree structure.
Each node has a unique identifier (id) and a parent node (parent), both integers.
"""

__slots__ = ('id', 'parent') # Memory optimization and faster attribute access

id: int
parent: int

def __init__(self, id: int, parent: int) -> None:
"""
Initialize a Node instance with id and parent.

Raises:
ValueError: If id or parent are not integers, or if parent is invalid.
"""
if not isinstance(id, int) or not isinstance(parent, int):
raise ValueError("Both id and parent must be integers.")
if parent == id:
raise ValueError("A node cannot be its own parent.")
if parent > id:
raise ValueError("Parent id cannot be greater than node id.")
self.id = id
self.parent = parent

def __repr__(self) -> str:
"""
Return the string representation of the Node that allows recreation using eval().
Example: Node(2, 1)
"""
return f"Node({self.id}, {self.parent})"

def __eq__(self, other: Any) -> bool:
"""
Compare Node equality based on id and parent.
"""
if not isinstance(other, Node):
return NotImplemented
return self.id == other.id and self.parent == other.parent

def __hash__(self) -> int:
"""
Provide a hash function to allow using Node in sets or as dict keys.
"""
return hash((self.id, self.parent))