Source code for sovabids.dicts

"""Module with dictionary utilities."""

import collections
from functools import reduce
[docs]def deep_get(dictionary, keys, default=None,sep='.'): """Safe nested dictionary getter. Parameters ---------- dictionary: dict The dictionary from which to get the value. keys: str The nested keys using sep as separator. Ie: 'person.name.lastname' if `sep`='.' default: object The default value to return if the key is not found sep : str, optional The separator to indicate nesting/branching/hierarchy. Returns ------- object: The value of the required key. `default` if the key is not found. Notes ----- Taken from https://stackoverflow.com/a/46890853/14068216 """ return reduce(lambda d, key: d.get(key, default) if isinstance(d, dict) else default, keys.split(sep), dictionary)
[docs]def deep_merge_N(l): """Merge the list of dictionaries, such that the latest one has the greater precedence. Parameters ---------- l : list of dict List containing the dictionaries to be merged, having precedence on the last ones. Returns ------- dict : The merged dictionary. """ d = {} while True: if len(l) == 0: return {} if len(l) == 1: return l[0] d1 = l.pop(0) d2 = l.pop(0) d = deep_merge(d1,d2) l.insert(0, d)
[docs]def deep_merge(a, b): """ Merge two values, with `b` taking precedence over `a`. Semantics: - If either `a` or `b` is not a dictionary, `a` will be returned only if `b` is `None`. Otherwise `b` will be returned. - If both values are dictionaries, they are merged as follows: * Each key that is found only in `a` or only in `b` will be included in the output collection with its value intact. * For any key in common between `a` and `b`, the corresponding values will be merged with the same semantics. From David Schneider answer at https://stackoverflow.com/questions/7204805/how-to-merge-dictionaries-of-dictionaries/15836901#15836901 Parameters ---------- a : object b : object Returns ------- dict : Merged dictionary. """ if not isinstance(a, dict) or not isinstance(b, dict): return a if b is None else b else: # If we're here, both a and b must be dictionaries or subtypes thereof. # Compute set of all keys in both dictionaries. keys = set(a.keys()) | set(b.keys()) # Build output dictionary, merging recursively values with common keys, # where `None` is used to mean the absence of a value. return { key: deep_merge(a.get(key), b.get(key)) for key in keys }
[docs]def flatten(d, parent_key='', sep='.'): """Flatten the nested dictionary structure using the given separator. If parent_key is given, then that level is added at the start of the tree. Parameters ---------- d : dict The dictionary to flat. parent_key : str, optional The optional top-level field of the dictionary. sep : str, optional The separator to indicate nesting/branching/hierarchy. Returns ------- dict : A dictionary with only one level of fields. """ items = [] for k, v in d.items(): new_key = parent_key + sep + k if parent_key else k if isinstance(v, collections.MutableMapping): items.extend(flatten(v, new_key, sep=sep).items()) else: items.append((new_key, v)) return dict(items)
[docs]def nested_notation_to_tree(key,value,leaf='.'): """Create a nested dictionary from the single (key,value) pair, with the key being branched by the leaf separator. Parameters ---------- key : str The key/field to be nested, assuming nesting is represented with the "leaf" parameters. value : object The value that it will have at the last level of nesting. leaf : str, optional The separator used to indicate nesting in "key" parameter. Returns ------- dict : Nested dictionary. """ if leaf in key: tree_list = key.split(leaf) tree_dict = value for key in reversed(tree_list): tree_dict = {key: tree_dict} return tree_dict else: return {key:value}