diff --git a/hypy_utils/dict_utils.py b/hypy_utils/dict_utils.py new file mode 100644 index 0000000..b33e279 --- /dev/null +++ b/hypy_utils/dict_utils.py @@ -0,0 +1,63 @@ + +def is_non_empty(o): + return not hasattr(o, '__len__') or len(o) > 0 + + +def remove_values(d: dict | list, vals: list, preserve_list: bool = False) -> dict | list: + """ + Recursively remove values from a dict + + :param d: Dict + :param vals: Values to remove + :param preserve_list: Whether to ignore list elements + :return: Dict without specific values + """ + if isinstance(d, list): + d = [remove_values(i, vals, preserve_list) for i in d if preserve_list or i not in vals] + d = [i for i in d if is_non_empty(i)] + return d + + if isinstance(d, dict): + d = {k: remove_values(v, vals, preserve_list) for k, v in d.items() if v not in vals} + d = {k: v for k, v in d.items() if is_non_empty(v)} + return d + + return d + + +def remove_nones(d: dict | list, preserve_list: bool = False) -> dict: + """ + Recursively remove nones from a dict + + >>> remove_nones({'a': {'b': None, 'c': 1}, 'b': [None, {'a': None}], 'c': {'a': None}, 'd': [None, 1]}) + {'a': {'c': 1}, 'd': [1]} + + :param d: Dict + :param preserve_list: Whether to ignore list elements + :return: Dict without nones + """ + return remove_values(d, [None], preserve_list=preserve_list) + + +def remove_keys(d: dict | list, keys: set) -> dict | list: + """ + Recursively remove keys + + >>> remove_keys({'a': {'b': None, 'c': 1}, 'b': [None, {'a': None}], 'c': {'a': None}, 'd': [None, 1]}, {'b'}) + {'a': {'c': 1}, 'c': {'a': None}, 'd': [None, 1]} + + :param d: The dictionary that you want to remove keys from + :param keys: Set of keys you want to remove + :return: Dict without specific keys + """ + if isinstance(d, list): + d = [remove_keys(i, keys) for i in d] + d = [i for i in d if is_non_empty(i)] + return d + + if isinstance(d, dict): + d = {k: remove_keys(v, keys) for k, v in d.items() if k not in keys} + d = {k: v for k, v in d.items() if is_non_empty(v)} + return d + + return d