Last active
February 13, 2023 09:53
-
-
Save bzah/90a47040b40b43a36fd4f3d3223038ca to your computer and use it in GitHub Desktop.
Flatten python dictionary and turn it into a RST table
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# This code is licensed under the terms of the APACHE 2 License (https://www.apache.org/licenses/LICENSE-2.0.html) | |
# Copyright (C) 2023 Aoun Abel aoun.abel@gmail.com | |
from __future__ import annotations | |
from functools import reduce | |
def flatten(dico: dict, separator: str = ".") -> dict: | |
""" Flatten a dictionary by creating a 1 level dict with key assembled using the given separator. | |
>>> flatten({"a":{"b":2, "c":{"d": 42}}, | |
"o":{"p":42}}) | |
>>> {'a.b': 2, 'a.c.d': 42, 'o.p': 42} | |
""" | |
flat_dict = {} | |
for k, v in dico.items(): | |
if isinstance(v, dict): | |
f = flatten(v) | |
for k2, v2 in f.items(): | |
flat_dict[k + separator + k2] = v2 | |
else: | |
flat_dict[k] = v | |
return flat_dict | |
def flatten_level_2(dico: dict): | |
""" Same as flatten but keep the first dict hierachy. | |
>>> flatten_level_2( | |
{"a":{"b":2, "c":{"d": 42}}, | |
"o":{"p":42}}) | |
>>> {'a': {'b': 2, 'c.d': 42}, 'o': {'p': 42}} | |
""" | |
for k, v in dico.items(): | |
if isinstance(v, dict): | |
dico[k] = flatten(v) | |
else: | |
dico[k] = v | |
return dico | |
def sort_by_key(dico: dict, key: str = "reference"): | |
"""Return a sorted dictionary, the resulting order is given by the items' `key` value. | |
""" | |
filtered = {k: v for k, v in dico.items() if v[key] is not None} | |
return {k: v for k, v in | |
sorted(filtered.items(), key=lambda x: x[1].get(key, "zzzz") or "zzzz")} | |
def filter_relevant_keys(dico: dict, keys: list[str])-> dict: | |
filtered_dico = {} | |
for k,v in dico.items(): | |
res = {} | |
for key in keys: | |
if key in v.keys(): | |
res.update({key: v[key] }) | |
filtered_dico.update({k: res}) | |
return filtered_dico | |
def to_rst_table(dico: dict[str, dict[str, str]], key="reference") -> None: | |
"""Turn a dictionary into a rst table. Not perfect | |
The output is printed in stdout. | |
""" | |
dico = sort_by_key(dico, key) | |
dico = flatten_level_2(dico) | |
dico = filter_relevant_keys(dico, [ | |
"reference", | |
"output.var_name", | |
"output.standard_name", | |
"output.cell_methods", | |
"output.long_name", | |
"output.units", | |
"output.proposed_standard_name", | |
"index_function.name", | |
"index_function.parameters.reducer.reducer", | |
]) | |
key_to_max_length_mapper = {} | |
inner_keys = reduce(lambda x,y : set(x) | set(y), map(lambda d: list(d.keys()), dico.values())) | |
# get maxes | |
for inner_key in inner_keys: | |
current_max = 0 | |
for k, v in dico.items(): | |
val = v.get(inner_key, "") | |
if len(str(val)) > current_max: | |
current_max = len(str(val)) | |
key_to_max_length_mapper.update({inner_key: current_max}) | |
# print table | |
mega_acc = "" | |
for k in inner_keys: | |
length = key_to_max_length_mapper.get(k) | |
end_space = " " * (length - len(k)) | |
mega_acc += f"{k}{end_space} |" | |
mega_acc+= "\n" | |
for k, v in dico.items(): | |
string_acc = "" | |
for k2 in inner_keys: | |
value = str(v.get(k2, "_") or "_") | |
length = key_to_max_length_mapper.get(k2) | |
end_space = " " * (length - len(value)) | |
string_acc += f" {value}{end_space} |" | |
mega_acc += "\n| " + string_acc | |
print(mega_acc) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment