You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

94 lines
2.4 KiB

"""
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