def partition(iteratee, seq): """ Return a ``tuple`` of 2 lists containing elements from `seq` split into two groups where the first group contains all elements the `iteratee` returned truthy for and the second group containing the falsey elements. Examples: >>> partition(lambda x: x % 2, [1, 2, 3, 4]) ([1, 3], [2, 4]) Args: iteratee (object): Iteratee applied per iteration. seq (Iterable): Iterable to iterate over. Returns: tuple[list] """ iteratee = fnc.iteratee(iteratee) successes = [] failures = [] for item in seq: if iteratee(item): successes.append(item) else: failures.append(item) return successes, failures
def groupby(iteratee, seq): """ Return a ``dict`` composed of keys generated from the results of running each element of `seq` through the `iteratee`. Examples: >>> result = groupby('a', [{'a': 1, 'b': 2}, {'a': 3, 'b': 4}]) >>> result == {1: [{'a': 1, 'b': 2}], 3: [{'a': 3, 'b': 4}]} True >>> result = groupby({'a': 1}, [{'a': 1, 'b': 2}, {'a': 3, 'b': 4}]) >>> result == {False: [{'a': 3, 'b': 4}], True: [{'a': 1, 'b': 2}]} True Args: iteratee (object): Iteratee applied per iteration. seq (Iterable): Iterable to iterate over. Returns: dict: Results of grouping by `iteratee`. """ result = {} iteratee = fnc.iteratee(iteratee) for item in seq: result.setdefault(iteratee(item), []).append(item) return result
def map(iteratee, *seqs): """ Map `iteratee` to each element of iterable and yield the results. If additional iterable arguments are passed, `iteratee` must take that many arguments and is applied to the items from all iterables in parallel. Note: This function is like the builtin ``map`` except it converts `iteratee` into a fnc-style predicate. Examples: >>> list(map(str, [1, 2, 3, 4])) ['1', '2', '3', '4'] >>> list(map('a', [{'a': 1, 'b': 2}, {'a': 3, 'b': 4}, {'a': 5, 'b': 6}])) [1, 3, 5] >>> list(map('0.1', [[[0, 1]], [[2, 3]], [[4, 5]]])) [1, 3, 5] >>> list(map('a.b', [{'a': {'b': 1}}, {'a': {'b': 2}}])) [1, 2] >>> list(map('a.b[1]', [{'a': {'b': [0, 1]}}, {'a': {'b': [2, 3]}}])) [1, 3] Args: iteratee (object): Iteratee applied per iteration. *seqs (Iterable): Iterables to map. Yields: Mapped elements. """ return _map(fnc.iteratee(iteratee), *seqs)
def intersectionby(iteratee, seq, *seqs): """ Like :func:`intersection` except that an `iteratee` is used to modify each element in the sequences. The modified values are then used for comparison. Note: This function is like ``set.intersection()`` except it works with both hashable and unhashable values and preserves the ordering of the original iterables. Examples: >>> list(intersectionby( ... 'a', ... [{'a': 1}, {'a': 2}, {'a': 3}], ... [{'a': 1}, {'a': 2}, {'a': 3}, {'a': 4}, {'a': 5}], ... [{'a': 2}, {'a': 3}] ... )) [{'a': 2}, {'a': 3}] Args: iteratee (object): Iteratee applied per iteration. seq (Iterable): Iterable to compute intersection against. *seqs (Iterable): Other iterables to compare with. Yields: Elements that intersect. """ if not seqs: yield from unionby(iteratee, seq) return if iteratee is not None: iteratee = fnc.iteratee(iteratee) yielded = Container() # Map iteratee to each item in each other sequence and compute intersection of those # values to reduce number of times iteratee is called. The resulting sequence will # be an intersection of computed values which will be used to compare to the primary # sequence. We'll store these values into a iterable in case any of the sequences # are a generator/iterator that would get exhausted if we tried to iterate over it # more than once. others = Container(intersection(*(map(iteratee, other) for other in seqs))) for item in seq: if iteratee is not None: value = iteratee(item) else: value = item if value in yielded: continue if value in others: yield item yielded.add(value)
def differenceby(iteratee, seq, *seqs): """ Like :func:`difference` except that an `iteratee` is used to modify each element in the sequences. The modified values are then used for comparison. Note: This function is like ``set.difference()`` except it works with both hashable and unhashable values and preserves the ordering of the original iterables. Examples: >>> list(differenceby('a', [{'a': 1}, {'a': 2}, {'a': 3}], [{'a': 1}], [{'a': 2}])) [{'a': 3}] >>> list(differenceby(lambda x: x % 4, [1, 4, 2, 3, 5, 0], [1], [2, 0])) [3] Args: iteratee (object): Iteratee applied per iteration. seq (Iterable): Iterable to compute difference against. *seqs (Iterable): Other iterables to compare with. Yields: Each element in `seq` that doesn't appear in `seqs`. """ if not seqs: yield from unionby(iteratee, seq) return if iteratee is not None: iteratee = fnc.iteratee(iteratee) yielded = Container() # Concat sequences into a single sequence and map iteratee to each item so that the # computed value only needs to be done once for each item since that is what we'll # compare to below. We'll store these values into a iterable in case any of the # sequences are a generator/iterator that would get exhausted if we tried to iterate # over it more than once. others = Container(map(iteratee, concat(*seqs))) for item in seq: if iteratee is not None: value = iteratee(item) else: value = item if value in yielded or value in others: continue yield item yielded.add(value)
def mapkeys(iteratee, obj): """ Return a ``dict`` with keys from `obj` mapped with `iteratee` while containing the same values. Examples: >>> result = mapkeys(lambda k: k * 2, {'a': 1, 'b': 2, 'c': 3}) >>> result == {'aa': 1, 'bb': 2, 'cc': 3} True Args: iteratee (object): Iteratee applied to each key. obj (Mapping): Mapping to map. Returns: dict: Dictionary with mapped keys. """ iteratee = fnc.iteratee(iteratee) return {iteratee(key): value for key, value in iterate(obj)}
def keyby(iteratee, seq): """ Return a ``dict`` composed of keys generated from the results of running each element of `seq` through the `iteratee`. Examples: >>> results = keyby('a', [{'a': 1, 'b': 2}, {'a': 3, 'b': 4}]) >>> results == {1: {'a': 1, 'b': 2}, 3: {'a': 3, 'b': 4}} True Args: iteratee (object): Iteratee applied per iteration. seq (Iterable): Iterable to iterate over. Returns: dict: Results of indexing by `iteratee`. """ iteratee = fnc.iteratee(iteratee) return {iteratee(value): value for value in seq}
def findindex(iteratee, seq): """ Return the index of the element in `seq` that returns ``True`` for `iteratee`. If no match is found, ``-1`` is returned. Examples: >>> findindex(lambda x: x >= 3, [1, 2, 3, 4]) 2 >>> findindex(lambda x: x > 4, [1, 2, 3, 4]) -1 Args: iteratee (object): Iteratee applied per iteration. seq (Iterable): Iterable to process. Returns: int: Index of found item or ``-1`` if not found. """ iteratee = fnc.iteratee(iteratee) return next((i for i, value in enumerate(seq) if iteratee(value)), -1)
def mapvalues(iteratee, obj): """ Return a ``dict`` with values from `obj` mapped with `iteratee` while containing the same keys. Examples: >>> result = mapvalues(lambda v: v * 2, {'a': 1, 'b': 2, 'c': 3}) >>> result == {'a': 2, 'b': 4, 'c': 6} True >>> result = mapvalues({'d': 4}, {'a': 1, 'b': {'d': 4}, 'c': 3}) >>> result == {'a': False, 'b': True, 'c': False} True Args: iteratee (object): Iteratee applied to each key. obj (Mapping): Mapping to map. Returns: dict: Dictionary with mapped values. """ iteratee = fnc.iteratee(iteratee) return {key: iteratee(value) for key, value in iterate(obj)}
def unionby(iteratee, seq, *seqs): """ Like :func:`union` except that an `iteratee` is used to modify each element in the sequences. The modified values are then used for comparison. Note: This function is like ``set.union()`` except it works with both hashable and unhashable values and preserves the ordering of the original iterables. Examples: >>> list(unionby( ... 'a', ... [{'a': 1}, {'a': 2}, {'a': 3}, {'a': 1}, {'a': 2}, {'a': 3}] ... )) [{'a': 1}, {'a': 2}, {'a': 3}] Args: iteratee (object): Iteratee applied per iteration. seq (Iterable): Iterable to compute union against. *seqs (Iterable): Other iterables to compare with. Yields: Each unique element from all iterables. """ if iteratee is not None: iteratee = fnc.iteratee(iteratee) seen = Container() for item in itertools.chain(seq, *seqs): if iteratee is not None: value = iteratee(item) else: value = item if value not in seen: yield item seen.add(value)
def reject(iteratee, seq): """ The opposite of :func:`filter` this function yields the elements of `seq` that the `iteratee` returns falsey for. Examples: >>> list(reject(lambda x: x >= 3, [1, 2, 3, 4])) [1, 2] >>> list(reject('a', [{'a': 0}, {'a': 1}, {'a': 2}])) [{'a': 0}] >>> list(reject({'a': 1}, [{'a': 0}, {'a': 1}, {'a': 2}])) [{'a': 0}, {'a': 2}] Args: iteratee (object): Iteratee applied per iteration. seq (Iterable): Iterable to iterate over. Yields: Rejected elements. """ iteratee = fnc.iteratee(iteratee) return filter(fnc.compose(iteratee, not_), seq)
def filter(iteratee, seq): """ Filter `seq` by `iteratee`, yielding only the elements that the iteratee returns truthy for. Note: This function is like the builtin ``filter`` except it converts `iteratee` into a fnc-style predicate. Examples: >>> result = filter({'a': 1}, [{'a': 1}, {'b': 2}, {'a': 1, 'b': 3}]) >>> list(result) == [{'a': 1}, {'a': 1, 'b': 3}] True >>> list(filter(lambda x: x >= 3, [1, 2, 3, 4])) [3, 4] Args: iteratee (object): Iteratee applied per iteration. seq (Iterable): Iterable to filter. Yields: Filtered elements. """ return _filter(fnc.iteratee(iteratee), seq)
def duplicatesby(iteratee, seq, *seqs): """ Like :func:`duplicates` except that an `iteratee` is used to modify each element in the sequences. The modified values are then used for comparison. Examples: >>> list(duplicatesby('a', [{'a':1}, {'a':3}, {'a':2}, {'a':3}, {'a':1}])) [{'a': 3}, {'a': 1}] Args: iteratee (object): Iteratee applied per iteration. seq (Iterable): Iterable to check for duplicates *seqs (Iterable): Other iterables to compare with. Yields: Each element in `seq` that doesn't appear in `seqs`. """ if iteratee is not None: iteratee = fnc.iteratee(iteratee) seen = Container() yielded = Container() for item in itertools.chain(seq, *seqs): if iteratee is not None: value = iteratee(item) else: value = item if value not in seen: seen.add(value) continue if value not in yielded: yield item yielded.add(value)
def test_iteratee(case): args = case["args"] assert fnc.iteratee(args[0])(*args[1:]) == case["expected"]