Repository: ManimCommunity/manim
File: manim/utils/iterables.py (Python)

Code before:
    """Alias for ``adjacent_n_tuples(objects, 2)``.

    See Also
    --------
    adjacent_n_tuples

    Examples
    --------
    .. code-block:: pycon

        >>> list(adjacent_pairs([1, 2, 3, 4]))
        [(1, 2), (2, 3), (3, 4), (4, 1)]
    """
    return adjacent_n_tuples(objects, 2)


def all_elements_are_instances(iterable: Iterable[object], Class: type[object]) -> bool:
    """Returns ``True`` if all elements of iterable are instances of Class.
    False otherwise.
    """
    return all(isinstance(e, Class) for e in iterable)


def batch_by_property(
    items: Sequence[T], property_func: Callable[[T], U]
) -> list[tuple[list[T], U]]:
    """Takes in a Sequence, and returns a list of tuples, (batch, prop)
    such that all items in a batch have the same output when
    put into the Callable property_func, and such that chaining all these
    batches together would give the original Sequence (i.e. order is
    preserved).

    Examples
    --------
    .. code-block:: pycon

        >>> batch_by_property([(1, 2), (3, 4), (5, 6, 7), (8, 9)], len)
        [([(1, 2), (3, 4)], 2), ([(5, 6, 7)], 3), ([(8, 9)], 2)]
    """
    batch_prop_pairs = []
    curr_batch = []
    curr_prop = None
    for item in items:
        prop = property_func(item)
        if prop != curr_prop:
            # Add current batch
            if len(curr_batch) > 0:
                batch_prop_pairs.append((curr_batch, curr_prop))
            # Redefine curr
            curr_prop = prop
            curr_batch = [item]

Review comment (performance):
```suggestion
    items: Iterable[T], property_func: Callable[[T], U]
) -> list[tuple[list[T], U]]:
```

I think it would also be great to deprecate this function, it is equivalent to [`itertools.groupby`](https://docs.python.org/3/library/itertools.html#itertools.groupby) (but way slower as pure Python). In this PR or a separate one?

Code after:
def adjacent_pairs(objects: Sequence[T]) -> zip[tuple[T, ...]]:
    """Alias for ``adjacent_n_tuples(objects, 2)``.

    See Also
    --------
    adjacent_n_tuples

    Examples
    --------
    .. code-block:: pycon

        >>> list(adjacent_pairs([1, 2, 3, 4]))
        [(1, 2), (2, 3), (3, 4), (4, 1)]
    """
    return adjacent_n_tuples(objects, 2)


def all_elements_are_instances(iterable: Iterable[object], Class: type[object]) -> bool:
    """Returns ``True`` if all elements of iterable are instances of Class.
    False otherwise.
    """
    return all(isinstance(e, Class) for e in iterable)


def batch_by_property(
    items: Iterable[T], property_func: Callable[[T], U]
) -> list[tuple[list[T], U | None]]:
    """Takes in a Sequence, and returns a list of tuples, (batch, prop)
    such that all items in a batch have the same output when
    put into the Callable property_func, and such that chaining all these
    batches together would give the original Sequence (i.e. order is
    preserved).

    Examples
    --------
    .. code-block:: pycon

        >>> batch_by_property([(1, 2), (3, 4), (5, 6, 7), (8, 9)], len)
        [([(1, 2), (3, 4)], 2), ([(5, 6, 7)], 3), ([(8, 9)], 2)]
    """
    batch_prop_pairs: list[tuple[list[T], U | None]] = []
    curr_batch: list[T] = []
    curr_prop = None
    for item in items:
        prop = property_func(item)
        if prop != curr_prop:
            # Add current batch
            if len(curr_batch) > 0:
                batch_prop_pairs.append((curr_batch, curr_prop))
            # Redefine curr
            curr_prop = prop