phml.utilities.locate.find

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
def ancestor(*nodes: phml.nodes.Node) -> phml.nodes.Node | None:
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.

def find( start: phml.nodes.Parent, condition: Union[list, str, dict, Callable[[phml.nodes.Node], bool]], strict: bool = True) -> phml.nodes.Node | None:
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.

def find_all( start: phml.nodes.Parent, condition: Union[list, str, dict, Callable[[phml.nodes.Node], bool]], strict: bool = True) -> list[phml.nodes.Node]:
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.

def find_after( start: phml.nodes.Node, condition: Union[list, str, dict, Callable[[phml.nodes.Node], bool], NoneType] = None, strict: bool = True) -> phml.nodes.Node | None:
 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.

def find_all_after( start: phml.nodes.Node, condition: Union[list, str, dict, Callable[[phml.nodes.Node], bool], NoneType] = None, strict: bool = True) -> list[phml.nodes.Node]:
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.

def find_all_before( start: phml.nodes.Node, condition: Union[list, str, dict, Callable[[phml.nodes.Node], bool], NoneType] = None, strict: bool = True) -> list[phml.nodes.Node]:
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.

def find_before( start: phml.nodes.Node, condition: Union[list, str, dict, Callable[[phml.nodes.Node], bool], NoneType] = None, strict: bool = True) -> phml.nodes.Node | None:
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.

def find_all_between( parent: phml.nodes.Parent, segment: tuple[int, int | None] = (0, None), condition: Union[list, str, dict, Callable[[phml.nodes.Node], bool], NoneType] = None, strict: bool = True) -> list[phml.nodes.Node]:
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.