Skip to content

Commit d41c01d

Browse files
Merge pull request #17 from StructuralPython/features/add_flatten_function
Features/add flatten function
2 parents d82e2ef + 9dffa88 commit d41c01d

File tree

4 files changed

+168
-57
lines changed

4 files changed

+168
-57
lines changed

src/jsonchain/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,6 @@
2121
filter_keys,
2222
merge_trees
2323
)
24-
from . import tables
24+
from . import tables
25+
26+
from .tables import flatten_tree

src/jsonchain/tables.py

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,4 +151,38 @@ def create_tree_table(
151151
tree_leaves.update({header_row[idx]: row[idx]})
152152
tree_branch.update(tree_leaves)
153153
return tree_acc
154-
154+
155+
156+
def flatten_tree(
157+
tree: dict,
158+
level_labels: list[str | float | int],
159+
_current_level=0,
160+
_current_dict={}
161+
) -> list[dict]:
162+
"""
163+
Returns a flattened list of dictionaries from the list.
164+
165+
tree: The nested dictionary to flatten. Must have the same number of
166+
depth of across all branches.
167+
level_labels: A list of strings to use as keys for each level.
168+
current_level: The current depth of the recursion.
169+
current_dict: The dictionary being built for the current path.
170+
"""
171+
flattened_list = []
172+
173+
# Iterate over the key-value pairs of the current dictionary level
174+
for key, value in tree.items():
175+
new_dict = _current_dict.copy()
176+
new_dict[level_labels[_current_level]] = key
177+
178+
# If the value is a dictionary and there are more levels to go, recurse
179+
if isinstance(value, dict) and _current_level + 1 < len(level_labels):
180+
flattened_list.extend(
181+
flatten_tree(value, level_labels, _current_level + 1, new_dict)
182+
)
183+
else:
184+
# Otherwise, we've reached a leaf node, so we update and append
185+
new_dict.update(value)
186+
flattened_list.append(new_dict)
187+
188+
return flattened_list

tests/test_jsonchain.py

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from jsonchain import load_json, dump_json, extract_keys
1+
from jsonchain import load_json, dump_json, extract_keys, flatten_tree
22
import pathlib
33

44
here = pathlib.Path.cwd()
@@ -31,4 +31,56 @@ def test_extract_keys():
3131
{"group": "aa"},
3232
{"group": "ab"},
3333
]
34-
34+
35+
36+
def test_flatten_tree():
37+
tree = {
38+
"a": {
39+
"A": {
40+
1: {
41+
"opta": 1,
42+
"optb": 2,
43+
"optc": 3,
44+
},
45+
2: {
46+
"opta": 10,
47+
"optb": 20,
48+
"optc": 30,
49+
},
50+
},
51+
"B": {
52+
1: {
53+
"opta": 21,
54+
"optb": 22,
55+
"optc": 23,
56+
},
57+
2: {
58+
"opta": 210,
59+
"optb": 220,
60+
"optc": 230,
61+
}
62+
},
63+
"C": {
64+
1: {
65+
"opta": 31,
66+
"optb": 32,
67+
"optc": 33,
68+
},
69+
2: {
70+
"opta": 310,
71+
"optb": 320,
72+
"optc": 330,
73+
}
74+
}
75+
}
76+
}
77+
flat = [
78+
{"member": "a", "force": "A", "case": 1, "opta": 1, "optb": 2, "optc": 3},
79+
{"member": "a", "force": "A", "case": 2, "opta": 10, "optb": 20, "optc": 30},
80+
{"member": "a", "force": "B", "case": 1, "opta": 21, "optb": 22, "optc": 23},
81+
{"member": "a", "force": "B", "case": 2, "opta": 210, "optb": 220, "optc": 230},
82+
{"member": "a", "force": "C", "case": 1, "opta": 31, "optb": 32, "optc": 33},
83+
{"member": "a", "force": "C", "case": 2, "opta": 310, "optb": 320, "optc": 330},
84+
]
85+
flattened = flatten_tree(tree, level_labels=['member', 'force', 'case'])
86+
assert flattened == flat

0 commit comments

Comments
 (0)