Skip to content

Commit 15cf2e5

Browse files
Merge pull request #1 from StructuralPython/feat-Add-enveloping
feat: implement enveloping
2 parents b3f1292 + 62db6aa commit 15cf2e5

File tree

1 file changed

+82
-0
lines changed

1 file changed

+82
-0
lines changed

src/jsonchain/envelope.py

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
from copy import copy
2+
from typing import Hashable
3+
4+
PYMAX = max
5+
PYMIN = min
6+
7+
def envelope_tree(tree: dict | list, levels: list[Hashable | None], leaf: Hashable | None, agg_func: callable, with_trace: bool = True) -> dict:
8+
"""
9+
Envelopes the tree at the leaf node with 'agg_func'.
10+
"""
11+
env_acc = {}
12+
# If we are at the last branch...
13+
if not levels:
14+
if isinstance(tree, list):
15+
tree = {idx: leaf for idx, leaf in enumerate(tree)}
16+
leaf_acc = {}
17+
for k, leaves in tree.items():
18+
if leaf is not None:
19+
leaf_acc.update({k: leaves[leaf]})
20+
else:
21+
leaf_acc = tree
22+
# ...create a dict of the enveloped value and the key
23+
# that it belongs to and return it.
24+
env_values = list(leaf_acc.values())
25+
env_value = agg_func(env_values)
26+
env_keys = list(leaf_acc.keys())
27+
try:
28+
env_key = env_keys[env_values.index(env_value)]
29+
except ValueError: # The value was transformed, likely due to abs()
30+
env_key = env_keys[env_values.index(-1 * env_value)]
31+
env_acc.update({"key": env_key, "value": env_value})
32+
if with_trace:
33+
return env_acc
34+
else:
35+
return env_value
36+
else:
37+
# Otherwise, pop the next level and dive into the tree on that branch
38+
level = levels[0]
39+
if level is not None:
40+
env_acc.update({level: envelope_tree(tree[level], levels[1:], leaf, agg_func, with_trace)})
41+
return env_acc
42+
else:
43+
# If None, then walk all branches of this node of the tree
44+
if isinstance(tree, list):
45+
tree = {idx: leaf for idx, leaf in enumerate(tree)}
46+
for k, v in tree.items():
47+
env_acc.update({k: envelope_tree(v, levels[1:], leaf, agg_func, with_trace)})
48+
return env_acc
49+
50+
51+
def absmax(x: list[float | int]) -> float | int:
52+
"""
53+
Returns the absolute maximum value in 'x'.
54+
"""
55+
abs_acc = [abs(y) for y in x]
56+
return max(abs_acc)
57+
58+
59+
def absmin(x: list[float | int]) -> float | int:
60+
"""
61+
Returns the absolute minimum value in 'x'.
62+
"""
63+
abs_acc = [abs(y) for y in x]
64+
return min(abs_acc)
65+
66+
67+
def max(x: list[float | int | None]) -> float | int | None:
68+
"""
69+
Returns the max value in 'x' while ignoring "None".
70+
If all values in 'x' are None, then None is returned.
71+
"""
72+
return PYMAX([y for y in x if y is not None])
73+
74+
75+
def min(x: list[float | int | None]) -> float | int | None:
76+
"""
77+
Returns the max value in 'x' while ignoring "None".
78+
If all values in 'x' are None, then None is returned.
79+
"""
80+
return PYMIN([y for y in x if y is not None])
81+
82+

0 commit comments

Comments
 (0)