Beispiel #1
0
def groupby_many(keys: Callable[[Any], Iterable], reducer: Reducer, initial):
    """Given a `keys` function, that maps an element into multiple keys, transduces the collection into a dictionary of key to group of matching elements.

    >>> transducer.transduce(
        transducer.groupby_many(
            lambda x: ("even",) if x % 2 == 0 else ("odd",),
            lambda s, x: (*s, x),
            (),
        ),
        lambda s, _: s,
        {},
        [1, 2, 3, 4, 5],
    )
    {"even": (2, 4), "odd": (1, 3, 5)}
    """
    return functional_generic.compose(
        mapcat(
            functional_generic.compose_left(
                functional_generic.juxt(keys, functional.wrap_tuple),
                sync.star(itertools.product),
            ), ),
        lambda step: lambda s, x: step(
            toolz.assoc(s, x[0], reducer(s.get(x[0], initial), x[1])),
            x,
        ),
    )
Beispiel #2
0
def explode(*positions: Collection[int]):
    """Flattens a non homogeneous iterable.

    For an iterable where some positions are iterable and some are not,
    "explodes" the iterable, so that each element appears in a single row, and duplicates the non iterable.

    >>> functional_generic.pipe(
        [
            "x",
            [
                "y1",
                "y2",
                "y3",
            ],
            "z",
        ],
        data.explode(1),
        tuple,
    )
    (
        ("x", "y1", "z"),
        ("x", "y2", "z"),
        ("x", "y3", "z"),
    )
    """
    return functional_generic.compose_left(
        _do_on_positions(
            functional.wrap_tuple,
            functional_generic.complement(functional.contains(positions)),
        ),
        sync.star(itertools.product),
    )
Beispiel #3
0
def groupby_many(f: Callable, it: Iterable) -> Dict[Text, Any]:
    """Return a mapping `{y: {x s.t. y in f(x)}}, where x in it. `

    Parameters:
    Key function (gets an object in collection and outputs tuple of keys).
    A Collection.

    Returns a dictionary where key has been computed by the `f` key function.

    >>> names = ['alice', 'bob', 'charlie', 'dan', 'edith', 'frank']
    >>> groupby_many(lambda name: (name[0], name[-1]), names)
    {'a': frozenset({'alice'}),
     'e': frozenset({'alice', 'charlie', 'edith'}),
     'b': frozenset({'bob'}),
     'c': frozenset({'charlie'}),
     'd': frozenset({'dan'}),
     'n': frozenset({'dan'}),
     'h': frozenset({'edith'}),
     'f': frozenset({'frank'}),
     'k': frozenset({'frank'})}"""
    return functional_generic.pipe(
        it,
        functional_generic.mapcat(
            functional_generic.compose_left(
                lambda element: (f(element), [element]),
                sync.star(itertools.product),
            ), ),
        edges_to_graph,
    )
Beispiel #4
0
async def test_itemfilter_async_sync_mixed():
    assert (await functional_generic.pipe(
        {
            1: 1,
            2: 1,
            3: 3
        },
        functional_generic.itemfilter(functional_generic.star(_equals)),
        functional_generic.itemfilter(sync.star(lambda _, val: val == 1)),
    ) == {
        1: 1
    })
Beispiel #5
0
async def test_async_bifurcate():
    async def async_sum(x):
        await asyncio.sleep(0.01)
        return sum(x)

    def gen():
        yield 1
        yield 2
        yield 3

    average = await functional_generic.pipe(
        gen(),
        functional_generic.bifurcate(async_sum, functional.count),
        sync.star(operator.truediv),
    )

    assert average == 2
Beispiel #6
0
def _get_children(element):
    return functional_generic.case_dict(
        {
            _is_terminal:
            functional.just(()),
            functional.is_instance(tuple):
            functional.identity,
            functional.is_instance(list):
            functional.identity,
            functional.is_instance(dict):
            functional_generic.compose_left(
                dict.items,
                functional.curried_map_sync(sync.star(KeyValue)),
            ),
            functional.is_instance(KeyValue):
            functional_generic.compose_left(
                lambda x: x.value,
                sync.ternary(
                    _is_terminal,
                    functional.wrap_tuple,
                    _get_children,
                ),
            ),
        }, )(element)
Beispiel #7
0
#: Combines transducers in a `dict` into a transducer that produces a `dict`.
#: >>> transducer.transduce(
#:     transducer.apply_spec(  # This will combine the inner stuff into one new transducer.
#:         {
#:             "incremented": _increment(_append_to_tuple),  # This is a transducer.
#:             "sum": lambda s, x: x + s,  # This is another transducer.
#:         },
#:     ),
#:     lambda s, _: s,
#:     {"incremented": (), "sum": 0},
#:     [1, 2, 3],
#: )
#: {"incremented": (2, 3, 4), "sum": 6}
apply_spec = functional_generic.compose_left(
    dict.items,
    sync.map(sync.star(_transform_by_key(toolz.assoc))),
    sync.star(functional_generic.compose),
)

#: Combines transducers in a `tuple` into a transducer that produces a `tuple`.
#: transducer.transduce(
#:     transducer.juxt(  # This will combine the inner stuff into one new transducer.
#:         _increment(_append_to_tuple),  # This is a transducer.
#:         lambda s, x: x + s,  # This is another transducer.
#:     ),
#:     lambda s, _: s,
#:     [(), 0],
#:     [1, 2, 3],
#: )
#: ((2, 3, 4), 6)
juxt = functional_generic.compose_left(
Beispiel #8
0
_MATCHED = "matched"
_UNMATCHED = "unmatched"
_get_matched = dict_utils.itemgetter(_MATCHED)
_get_unmatched = dict_utils.itemgetter(_UNMATCHED)


def _make_matched_unmatched(matched, unmatched):
    return {_MATCHED: matched, _UNMATCHED: unmatched}


_merge_children_as_matched = functional_generic.compose_left(
    sync.mapcat(sync.juxtcat(_get_matched, _get_unmatched)),
    tuple,
    functional_generic.pair_right(functional.just(())),
    sync.star(_make_matched_unmatched),
)

_merge_children = sync.compose_left(
    functional_generic.bifurcate(
        sync.compose_left(sync.mapcat(_get_matched), tuple),
        sync.compose_left(
            sync.mapcat(_get_unmatched),
            tuple,
        ),
    ),
    sync.star(_make_matched_unmatched),
)


@currying.curry
Beispiel #9
0
        lambda applier: map_dict(functional.identity, applier),
        apply_utils.apply(spec),
    )


#: Construct a function that applies the i'th function in an iterable
#  on the i'th element of a given iterable
#:
#: Note: Number of functions should be equal to the number of elements in the given iterable
#:
#: >>> stack([lambda x:x+1, lambda x:x-1])((5, 5))
#: (6, 4)
stack = compose_left(
    enumerate,
    functional.curried_map_sync(
        sync.star(lambda i, f: compose(f, lambda x: x[i])),
    ),
    sync.star(juxt),
)


def bifurcate(*funcs):
    """Serially run each function on tee'd copies of a sequence.
    If the sequence is a generator, it is duplicated so it will not be exhausted (which may incur a substantial memory signature in some cases).
    >>> f = bifurcate(sum, gamla.count)
    >>> seq = map(gamla.identity, [1, 2, 3, 4, 5])
    >>> f(seq)
    (15, 5)
    """
    return compose_left(iter, lambda it: itertools.tee(it, len(funcs)), stack(funcs))
Beispiel #10
0
edges_to_graph = functional_generic.compose(
    functional_generic.valmap(
        functional_generic.compose(
            frozenset,
            functional_generic.curried_map(functional.second),
        ), ),
    sync.groupby(functional.head),
)
#: Gets a graph and returns an iterator of all edges in it.
#:
#:  >>> list(graph_to_edges({'1': ['2', '3'], '2': ['3'], '3': ['4'], '4': []}))
#:  [('1', '2'), ('1', '3'), ('2', '3'), ('3', '4')]
graph_to_edges = functional_generic.compose_left(
    sync.keymap(functional.wrap_tuple),
    dict.items,
    sync.mapcat(sync.star(itertools.product)),
)

#: Gets a graph and returns the graph with its edges reversed
#:
#:  >>> reverse_graph({'1': ['2', '3'], '2': ['3'], '3': ['4'], '4': []})
#:  {'2': frozenset({'1'}), '3': frozenset({'1', '2'}), '4': frozenset({'3'})}
reverse_graph = functional_generic.compose_left(
    graph_to_edges,
    functional_generic.curried_map(
        functional_generic.compose_left(reversed, tuple)),
    edges_to_graph,
)

#: Gets a sequence of nodes (cliques) and returns the bidirectional graph they represent
#:
Beispiel #11
0
def _debug_generic(f):
    return functional_generic.compose_left(
        lambda *funcs: toolz.interleave([funcs, [debug] * len(funcs)]),
        sync.star(f),
    )