""" A collection of functional utilities/helpers """ from functools import reduce from copy import deepcopy from itertools import chain, dropwhile def getin(m, path, default=None): """returns the value in a nested dict""" keynotfound = ':com.gooey-project/not-found' result = reduce(lambda acc, val: acc.get(val, {keynotfound: None}), path, m) # falsey values like 0 would incorrectly trigger the default to be returned # so the keynotfound val is used to signify a miss vs just a falesy val if isinstance(result, dict) and keynotfound in result: return default return result def assoc(m, key, val): """Copy-on-write associates a value in a dict""" cpy = deepcopy(m) cpy[key] = val return cpy def associn(m, path, value): """ Copy-on-write associates a value in a nested dict """ def assoc_recursively(m, path, value): if not path: return value p = path[0] return assoc(m, p, assoc_recursively(m.get(p,{}), path[1:], value)) return assoc_recursively(m, path, value) def merge(*maps): """Merge all maps left to right""" copies = map(deepcopy, maps) return reduce(lambda acc, val: acc.update(val) or acc, copies) def flatmap(f, coll): """Applies concat to the result of applying f to colls""" return list(chain(*map(f, coll))) def indexunique(f, coll): """Build a map from the collection keyed off of f e.g. [{id:1,..}, {id:2, ...}] => {1: {id:1,...}, 2: {id:2,...}} Note: duplicates, if present, are overwritten """ return zipmap(map(f, coll), coll) def findfirst(f, coll): """Return first occurrence matching f, otherwise None""" result = list(dropwhile(f, coll)) return result[0] if result else None def zipmap(keys, vals): """Return a map from keys to values""" return dict(zip(keys, vals)) def compact(coll): """Returns a new list with all falsy values removed""" return list(filter(None, coll)) def ifPresent(f): """Execute f only if value is present and not None""" def inner(value): if value: return f(value) else: return True return inner def identity(x): """Identity function always returns the supplied argument""" return x def unit(val): return val def bind(val, f): return f(val) if val else None