phml.utilities.locate.find
Collection of utility methods to find one or many of a specific node.
1"""phml.utilities.locate.find 2 3Collection of utility methods to find one or many of a specific node. 4""" 5 6from phml.nodes import Node, Parent 7from phml.utilities.travel.travel import path, walk 8from phml.utilities.validate import Test, check 9 10__all__ = [ 11 "ancestor", 12 "find", 13 "find_all", 14 "find_after", 15 "find_all_after", 16 "find_all_before", 17 "find_before", 18 "find_all_between", 19] 20 21 22def ancestor(*nodes: Node) -> Node | None: 23 """Get the common ancestor between two nodes. 24 25 Args: 26 *nodes (Node): A list of any number of nodes 27 to find the common ancestor form. Worst case it will 28 return the root. 29 30 Returns: 31 Optional[Node]: The node that is the common 32 ancestor or None if not found. 33 """ 34 total_path: list | None = None 35 36 def filter_func(node, total_path) -> bool: 37 return node in total_path 38 39 for node in nodes: 40 if total_path is not None: 41 total_path = list(filter(lambda n: filter_func(n, total_path), path(node))) 42 else: 43 total_path = path(node) 44 45 total_path = total_path or [] 46 return total_path[-1] if len(total_path) > 0 else None 47 48 49def find(start: Parent, condition: Test, strict: bool = True) -> Node | None: 50 """Walk the nodes children and return the desired node. 51 52 Returns the first node that matches the condition. 53 54 Args: 55 start (Parent): Starting node. 56 condition (Test): Condition to check against each node. 57 58 Returns: 59 Optional[Node]: Returns the found node or None if not found. 60 """ 61 for node in walk(start): 62 if check(node, condition, strict=strict): 63 return node 64 65 return None 66 67 68def find_all(start: Parent, condition: Test, strict: bool = True) -> list[Node]: 69 """Find all nodes that match the condition. 70 71 Args: 72 start (Root | Element): Starting node. 73 condition (Test): Condition to apply to each node. 74 75 Returns: 76 list[Node]: List of found nodes. Empty if no nodes are found. 77 """ 78 results = [] 79 for node in walk(start): 80 if check(node, condition, strict=strict): 81 results.append(node) 82 return results 83 84 85def find_after( 86 start: Node, 87 condition: Test | None = None, 88 strict: bool = True, 89) -> Node | None: 90 """Get the first sibling node following the provided node that matches 91 the condition. 92 93 Args: 94 start (Node): Node to get sibling from. 95 condition (Test): Condition to check against each node. 96 97 Returns: 98 Optional[Node]: Returns the first sibling or None if there 99 are no siblings. 100 """ 101 102 if start.parent is not None: 103 idx = start.parent.index(start) 104 if len(start.parent) - 1 > idx: 105 for node in start.parent[idx + 1 :]: 106 if condition is not None: 107 if check(node, condition, strict=strict): 108 return node 109 else: 110 return node 111 return None 112 113 114def find_all_after( 115 start: Node, 116 condition: Test | None = None, 117 strict: bool = True, 118) -> list[Node]: 119 """Get all sibling nodes that match the condition. 120 121 Args: 122 start (Node): Node to get siblings from. 123 condition (Test): Condition to check against each node. 124 125 Returns: 126 list[Node]: Returns the all siblings that match the 127 condition or an empty list if none were found. 128 """ 129 if start.parent is None: 130 return [] 131 132 idx = start.parent.index(start) 133 matches = [] 134 135 if len(start.parent) - 1 > idx: 136 for node in start.parent[idx + 1 :]: 137 if condition is not None: 138 if check(node, condition, strict=strict): 139 matches.append(node) 140 else: 141 matches.append(node) 142 143 return matches 144 145 146def find_before( 147 start: Node, 148 condition: Test | None = None, 149 strict: bool = True, 150) -> Node | None: 151 """Find the first sibling node before the given node. If a condition is applied 152 then it will be the first sibling node that passes that condition. 153 154 Args: 155 start (Node): The node to find the previous sibling from. 156 condition (Optional[Test]): The test that is applied to each node. 157 158 Returns: 159 Optional[Node]: The first node before the given node 160 or None if no prior siblings. 161 """ 162 163 if start.parent is not None: 164 idx = start.parent.index(start) 165 if idx > 0: 166 for node in start.parent[idx - 1 :: -1]: 167 if condition is not None: 168 if check(node, condition, strict=strict): 169 return node 170 else: 171 return node 172 return None 173 174 175def find_all_before( 176 start: Node, 177 condition: Test | None = None, 178 strict: bool = True, 179) -> list[Node]: 180 """Find all nodes that come before the given node. 181 182 Args: 183 start (Node): The node to find all previous siblings from. 184 condition (Optional[Test]): The condition to apply to each node. 185 186 Returns: 187 list[Node]: A list of nodes that come before the given node. 188 Empty list if no nodes were found. 189 """ 190 if start.parent is None: 191 return [] 192 193 idx = start.parent.index(start) 194 matches = [] 195 196 if idx > 0: 197 for node in start.parent[:idx]: 198 if condition is not None: 199 if check(node, condition, strict=strict): 200 matches.append(node) 201 else: 202 matches.append(node) 203 return matches 204 205 206def find_all_between( 207 parent: Parent, 208 segment: tuple[int, int | None] = (0, None), 209 condition: Test | None = None, 210 strict: bool = True, 211) -> list[Node]: 212 """Find all sibling nodes in parent that meet the provided condition from start index 213 to end index. 214 215 Args: 216 parent (Parent): The parent element to get nodes from. 217 start (int, optional): The starting index, inclusive. Defaults to 0. 218 end (int, optional): The ending index, exclusive. Defaults to 0. 219 condition (Test, optional): Condition to apply to each node. Defaults to None. 220 221 Returns: 222 list[Node]: List of all matching nodes or an empty list if none were found. 223 """ 224 _range = slice(segment[0], segment[1] or len(parent)) 225 226 results = [] 227 if _range.start < len(parent) and _range.stop <= len(parent): 228 for node in parent[_range]: 229 if condition is not None: 230 if check(node, condition, strict=strict): 231 results.append(node) 232 else: 233 results.append(node) 234 return results
23def ancestor(*nodes: Node) -> Node | None: 24 """Get the common ancestor between two nodes. 25 26 Args: 27 *nodes (Node): A list of any number of nodes 28 to find the common ancestor form. Worst case it will 29 return the root. 30 31 Returns: 32 Optional[Node]: The node that is the common 33 ancestor or None if not found. 34 """ 35 total_path: list | None = None 36 37 def filter_func(node, total_path) -> bool: 38 return node in total_path 39 40 for node in nodes: 41 if total_path is not None: 42 total_path = list(filter(lambda n: filter_func(n, total_path), path(node))) 43 else: 44 total_path = path(node) 45 46 total_path = total_path or [] 47 return total_path[-1] if len(total_path) > 0 else None
Get the common ancestor between two nodes.
Args
- *nodes (Node): A list of any number of nodes
- to find the common ancestor form. Worst case it will
- return the root.
Returns
Optional[Node]: The node that is the common ancestor or None if not found.
50def find(start: Parent, condition: Test, strict: bool = True) -> Node | None: 51 """Walk the nodes children and return the desired node. 52 53 Returns the first node that matches the condition. 54 55 Args: 56 start (Parent): Starting node. 57 condition (Test): Condition to check against each node. 58 59 Returns: 60 Optional[Node]: Returns the found node or None if not found. 61 """ 62 for node in walk(start): 63 if check(node, condition, strict=strict): 64 return node 65 66 return None
Walk the nodes children and return the desired node.
Returns the first node that matches the condition.
Args
- start (Parent): Starting node.
- condition (Test): Condition to check against each node.
Returns
Optional[Node]: Returns the found node or None if not found.
69def find_all(start: Parent, condition: Test, strict: bool = True) -> list[Node]: 70 """Find all nodes that match the condition. 71 72 Args: 73 start (Root | Element): Starting node. 74 condition (Test): Condition to apply to each node. 75 76 Returns: 77 list[Node]: List of found nodes. Empty if no nodes are found. 78 """ 79 results = [] 80 for node in walk(start): 81 if check(node, condition, strict=strict): 82 results.append(node) 83 return results
Find all nodes that match the condition.
Args
- start (Root | Element): Starting node.
- condition (Test): Condition to apply to each node.
Returns
list[Node]: List of found nodes. Empty if no nodes are found.
86def find_after( 87 start: Node, 88 condition: Test | None = None, 89 strict: bool = True, 90) -> Node | None: 91 """Get the first sibling node following the provided node that matches 92 the condition. 93 94 Args: 95 start (Node): Node to get sibling from. 96 condition (Test): Condition to check against each node. 97 98 Returns: 99 Optional[Node]: Returns the first sibling or None if there 100 are no siblings. 101 """ 102 103 if start.parent is not None: 104 idx = start.parent.index(start) 105 if len(start.parent) - 1 > idx: 106 for node in start.parent[idx + 1 :]: 107 if condition is not None: 108 if check(node, condition, strict=strict): 109 return node 110 else: 111 return node 112 return None
Get the first sibling node following the provided node that matches the condition.
Args
- start (Node): Node to get sibling from.
- condition (Test): Condition to check against each node.
Returns
Optional[Node]: Returns the first sibling or None if there are no siblings.
115def find_all_after( 116 start: Node, 117 condition: Test | None = None, 118 strict: bool = True, 119) -> list[Node]: 120 """Get all sibling nodes that match the condition. 121 122 Args: 123 start (Node): Node to get siblings from. 124 condition (Test): Condition to check against each node. 125 126 Returns: 127 list[Node]: Returns the all siblings that match the 128 condition or an empty list if none were found. 129 """ 130 if start.parent is None: 131 return [] 132 133 idx = start.parent.index(start) 134 matches = [] 135 136 if len(start.parent) - 1 > idx: 137 for node in start.parent[idx + 1 :]: 138 if condition is not None: 139 if check(node, condition, strict=strict): 140 matches.append(node) 141 else: 142 matches.append(node) 143 144 return matches
Get all sibling nodes that match the condition.
Args
- start (Node): Node to get siblings from.
- condition (Test): Condition to check against each node.
Returns
list[Node]: Returns the all siblings that match the condition or an empty list if none were found.
176def find_all_before( 177 start: Node, 178 condition: Test | None = None, 179 strict: bool = True, 180) -> list[Node]: 181 """Find all nodes that come before the given node. 182 183 Args: 184 start (Node): The node to find all previous siblings from. 185 condition (Optional[Test]): The condition to apply to each node. 186 187 Returns: 188 list[Node]: A list of nodes that come before the given node. 189 Empty list if no nodes were found. 190 """ 191 if start.parent is None: 192 return [] 193 194 idx = start.parent.index(start) 195 matches = [] 196 197 if idx > 0: 198 for node in start.parent[:idx]: 199 if condition is not None: 200 if check(node, condition, strict=strict): 201 matches.append(node) 202 else: 203 matches.append(node) 204 return matches
Find all nodes that come before the given node.
Args
- start (Node): The node to find all previous siblings from.
- condition (Optional[Test]): The condition to apply to each node.
Returns
list[Node]: A list of nodes that come before the given node. Empty list if no nodes were found.
147def find_before( 148 start: Node, 149 condition: Test | None = None, 150 strict: bool = True, 151) -> Node | None: 152 """Find the first sibling node before the given node. If a condition is applied 153 then it will be the first sibling node that passes that condition. 154 155 Args: 156 start (Node): The node to find the previous sibling from. 157 condition (Optional[Test]): The test that is applied to each node. 158 159 Returns: 160 Optional[Node]: The first node before the given node 161 or None if no prior siblings. 162 """ 163 164 if start.parent is not None: 165 idx = start.parent.index(start) 166 if idx > 0: 167 for node in start.parent[idx - 1 :: -1]: 168 if condition is not None: 169 if check(node, condition, strict=strict): 170 return node 171 else: 172 return node 173 return None
Find the first sibling node before the given node. If a condition is applied then it will be the first sibling node that passes that condition.
Args
- start (Node): The node to find the previous sibling from.
- condition (Optional[Test]): The test that is applied to each node.
Returns
Optional[Node]: The first node before the given node or None if no prior siblings.
207def find_all_between( 208 parent: Parent, 209 segment: tuple[int, int | None] = (0, None), 210 condition: Test | None = None, 211 strict: bool = True, 212) -> list[Node]: 213 """Find all sibling nodes in parent that meet the provided condition from start index 214 to end index. 215 216 Args: 217 parent (Parent): The parent element to get nodes from. 218 start (int, optional): The starting index, inclusive. Defaults to 0. 219 end (int, optional): The ending index, exclusive. Defaults to 0. 220 condition (Test, optional): Condition to apply to each node. Defaults to None. 221 222 Returns: 223 list[Node]: List of all matching nodes or an empty list if none were found. 224 """ 225 _range = slice(segment[0], segment[1] or len(parent)) 226 227 results = [] 228 if _range.start < len(parent) and _range.stop <= len(parent): 229 for node in parent[_range]: 230 if condition is not None: 231 if check(node, condition, strict=strict): 232 results.append(node) 233 else: 234 results.append(node) 235 return results
Find all sibling nodes in parent that meet the provided condition from start index to end index.
Args
- parent (Parent): The parent element to get nodes from.
- start (int, optional): The starting index, inclusive. Defaults to 0.
- end (int, optional): The ending index, exclusive. Defaults to 0.
- condition (Test, optional): Condition to apply to each node. Defaults to None.
Returns
list[Node]: List of all matching nodes or an empty list if none were found.