aboutsummaryrefslogtreecommitdiff
path: root/.venv/lib/python3.12/site-packages/pydash/arrays.py
diff options
context:
space:
mode:
authorS. Solomon Darnell2025-03-28 21:52:21 -0500
committerS. Solomon Darnell2025-03-28 21:52:21 -0500
commit4a52a71956a8d46fcb7294ac71734504bb09bcc2 (patch)
treeee3dc5af3b6313e921cd920906356f5d4febc4ed /.venv/lib/python3.12/site-packages/pydash/arrays.py
parentcc961e04ba734dd72309fb548a2f97d67d578813 (diff)
downloadgn-ai-4a52a71956a8d46fcb7294ac71734504bb09bcc2.tar.gz
two version of R2R are hereHEADmaster
Diffstat (limited to '.venv/lib/python3.12/site-packages/pydash/arrays.py')
-rw-r--r--.venv/lib/python3.12/site-packages/pydash/arrays.py2964
1 files changed, 2964 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/pydash/arrays.py b/.venv/lib/python3.12/site-packages/pydash/arrays.py
new file mode 100644
index 00000000..2bc3bc94
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pydash/arrays.py
@@ -0,0 +1,2964 @@
+"""
+Functions that operate on lists.
+
+.. versionadded:: 1.0.0
+"""
+
+from __future__ import annotations
+
+from bisect import bisect_left, bisect_right
+from functools import cmp_to_key
+from math import ceil
+import typing as t
+
+import pydash as pyd
+
+from .helpers import base_get, iteriteratee, parse_iteratee
+from .types import IterateeObjT
+
+
+if t.TYPE_CHECKING:
+ from _typeshed import SupportsRichComparisonT # pragma: no cover
+
+
+__all__ = (
+ "chunk",
+ "compact",
+ "concat",
+ "difference",
+ "difference_by",
+ "difference_with",
+ "drop",
+ "drop_right",
+ "drop_right_while",
+ "drop_while",
+ "duplicates",
+ "fill",
+ "find_index",
+ "find_last_index",
+ "flatten",
+ "flatten_deep",
+ "flatten_depth",
+ "from_pairs",
+ "head",
+ "index_of",
+ "initial",
+ "intercalate",
+ "interleave",
+ "intersection",
+ "intersection_by",
+ "intersection_with",
+ "intersperse",
+ "last",
+ "last_index_of",
+ "mapcat",
+ "nth",
+ "pull",
+ "pull_all",
+ "pull_all_by",
+ "pull_all_with",
+ "pull_at",
+ "push",
+ "remove",
+ "reverse",
+ "shift",
+ "slice_",
+ "sort",
+ "sorted_index",
+ "sorted_index_by",
+ "sorted_index_of",
+ "sorted_last_index",
+ "sorted_last_index_by",
+ "sorted_last_index_of",
+ "sorted_uniq",
+ "sorted_uniq_by",
+ "splice",
+ "split_at",
+ "tail",
+ "take",
+ "take_right",
+ "take_right_while",
+ "take_while",
+ "union",
+ "union_by",
+ "union_with",
+ "uniq",
+ "uniq_by",
+ "uniq_with",
+ "unshift",
+ "unzip",
+ "unzip_with",
+ "without",
+ "xor",
+ "xor_by",
+ "xor_with",
+ "zip_",
+ "zip_object",
+ "zip_object_deep",
+ "zip_with",
+)
+
+T = t.TypeVar("T")
+T2 = t.TypeVar("T2")
+T3 = t.TypeVar("T3")
+T4 = t.TypeVar("T4")
+T5 = t.TypeVar("T5")
+SequenceT = t.TypeVar("SequenceT", bound=t.Sequence[t.Any])
+MutableSequenceT = t.TypeVar("MutableSequenceT", bound=t.MutableSequence[t.Any])
+
+
+def chunk(array: t.Sequence[T], size: int = 1) -> t.List[t.Sequence[T]]:
+ """
+ Creates a list of elements split into groups the length of `size`. If `array` can't be split
+ evenly, the final chunk will be the remaining elements.
+
+ Args:
+ array: List to chunk.
+ size: Chunk size. Defaults to ``1``.
+
+ Returns:
+ New list containing chunks of `array`.
+
+ Example:
+
+ >>> chunk([1, 2, 3, 4, 5], 2)
+ [[1, 2], [3, 4], [5]]
+
+ .. versionadded:: 1.1.0
+ """
+ chunks = int(ceil(len(array) / float(size)))
+ return [array[i * size : (i + 1) * size] for i in range(chunks)]
+
+
+def compact(array: t.Iterable[t.Union[T, None]]) -> t.List[T]:
+ """
+ Creates a list with all falsey values of array removed.
+
+ Args:
+ array: List to compact.
+
+ Returns:
+ Compacted list.
+
+ Example:
+
+ >>> compact(["", 1, 0, True, False, None])
+ [1, True]
+
+ .. versionadded:: 1.0.0
+ """
+ return [item for item in array if item]
+
+
+def concat(*arrays: t.Iterable[T]) -> t.List[T]:
+ """
+ Concatenates zero or more lists into one.
+
+ Args:
+ arrays: Lists to concatenate.
+
+ Returns:
+ Concatenated list.
+
+ Example:
+
+ >>> concat([1, 2], [3, 4], [[5], [6]])
+ [1, 2, 3, 4, [5], [6]]
+
+ .. versionadded:: 2.0.0
+
+ .. versionchanged:: 4.0.0
+ Renamed from ``cat`` to ``concat``.
+ """
+ return flatten(arrays)
+
+
+def difference(array: t.Iterable[T], *others: t.Iterable[T]) -> t.List[T]:
+ """
+ Creates a list of list elements not present in others.
+
+ Args:
+ array: List to process.
+ others: Lists to check.
+
+ Returns:
+ Difference between `others`.
+
+ Example:
+
+ >>> difference([1, 2, 3], [1], [2])
+ [3]
+
+ .. versionadded:: 1.0.0
+ """
+ return difference_with(array, *others)
+
+
+@t.overload
+def difference_by(
+ array: t.Iterable[T],
+ *others: t.Iterable[T],
+ iteratee: t.Union[IterateeObjT, t.Callable[[T], t.Any], None],
+) -> t.List[T]: ...
+
+
+@t.overload
+def difference_by(
+ array: t.Iterable[T], *others: t.Union[IterateeObjT, t.Iterable[T], t.Callable[[T], t.Any]]
+) -> t.List[T]: ...
+
+
+def difference_by(array, *others, **kwargs):
+ """
+ This method is like :func:`difference` except that it accepts an iteratee which is invoked for
+ each element of each array to generate the criterion by which they're compared. The order and
+ references of result values are determined by `array`. The iteratee is invoked with one
+ argument: ``(value)``.
+
+ Args:
+ array: The array to find the difference of.
+ others: Lists to check for difference with `array`.
+
+ Keyword Args:
+ iteratee: Function to transform the elements of the arrays. Defaults to
+ :func:`.identity`.
+
+ Returns:
+ Difference between `others`.
+
+ Example:
+
+ >>> difference_by([1.2, 1.5, 1.7, 2.8], [0.9, 3.2], round)
+ [1.5, 1.7]
+
+ .. versionadded:: 4.0.0
+ """
+ array = array[:]
+
+ if not others:
+ return array
+
+ # Check if last other is a potential iteratee.
+ iteratee, others = parse_iteratee("iteratee", *others, **kwargs)
+
+ for other in others:
+ if not other:
+ continue
+ array = list(iterdifference(array, other, iteratee=iteratee))
+
+ return array
+
+
+@t.overload
+def difference_with(
+ array: t.Iterable[T],
+ *others: t.Iterable[T2],
+ comparator: t.Union[t.Callable[[T, T2], t.Any], None],
+) -> t.List[T]: ...
+
+
+@t.overload
+def difference_with(
+ array: t.Iterable[T], *others: t.Union[t.Iterable[T2], t.Callable[[T, T2], t.Any]]
+) -> t.List[T]: ...
+
+
+def difference_with(array, *others, **kwargs):
+ """
+ This method is like :func:`difference` except that it accepts a comparator which is invoked to
+ compare the elements of all arrays. The order and references of result values are determined by
+ the first array. The comparator is invoked with two arguments: ``(arr_val, oth_val)``.
+
+ Args:
+ array: The array to find the difference of.
+ others: Lists to check for difference with `array`.
+
+ Keyword Args:
+ comparator: Function to compare the elements of the arrays. Defaults to
+ :func:`.is_equal`.
+
+ Returns:
+ Difference between `others`.
+
+ Example:
+
+ >>> array = ["apple", "banana", "pear"]
+ >>> others = (["avocado", "pumpkin"], ["peach"])
+ >>> comparator = lambda a, b: a[0] == b[0]
+ >>> difference_with(array, *others, comparator=comparator)
+ ['banana']
+
+ .. versionadded:: 4.0.0
+ """
+ array = array[:]
+
+ if not others:
+ return array
+
+ comparator = kwargs.get("comparator")
+ last_other = others[-1]
+
+ # Check if last other is a comparator.
+ if comparator is None and (callable(last_other) or last_other is None):
+ comparator = last_other
+ others = others[:-1]
+
+ for other in others:
+ if not other:
+ continue
+ array = list(iterdifference(array, other, comparator=comparator))
+
+ return array
+
+
+def drop(array: t.Sequence[T], n: int = 1) -> t.List[T]:
+ """
+ Creates a slice of `array` with `n` elements dropped from the beginning.
+
+ Args:
+ array: List to process.
+ n: Number of elements to drop. Defaults to ``1``.
+
+ Returns:
+ Dropped list.
+
+ Example:
+
+ >>> drop([1, 2, 3, 4], 2)
+ [3, 4]
+
+ .. versionadded:: 1.0.0
+
+ .. versionchanged:: 1.1.0
+ Added ``n`` argument and removed as alias of :func:`rest`.
+
+ .. versionchanged:: 3.0.0
+ Made ``n`` default to ``1``.
+ """
+ return drop_while(array, lambda _, index: index < n)
+
+
+def drop_right(array: t.Sequence[T], n: int = 1) -> t.List[T]:
+ """
+ Creates a slice of `array` with `n` elements dropped from the end.
+
+ Args:
+ array: List to process.
+ n: Number of elements to drop. Defaults to ``1``.
+
+ Returns:
+ Dropped list.
+
+ Example:
+
+ >>> drop_right([1, 2, 3, 4], 2)
+ [1, 2]
+
+ .. versionadded:: 1.1.0
+
+ .. versionchanged:: 3.0.0
+ Made ``n`` default to ``1``.
+ """
+ length = len(array)
+ return drop_right_while(array, lambda _, index: (length - index) <= n)
+
+
+@t.overload
+def drop_right_while(
+ array: t.Sequence[T], predicate: t.Callable[[T, int, t.List[T]], t.Any]
+) -> t.List[T]: ...
+
+
+@t.overload
+def drop_right_while(array: t.Sequence[T], predicate: t.Callable[[T, int], t.Any]) -> t.List[T]: ...
+
+
+@t.overload
+def drop_right_while(array: t.Sequence[T], predicate: t.Callable[[T], t.Any]) -> t.List[T]: ...
+
+
+@t.overload
+def drop_right_while(array: t.Sequence[T], predicate: None = None) -> t.List[T]: ...
+
+
+def drop_right_while(array, predicate=None):
+ """
+ Creates a slice of `array` excluding elements dropped from the end. Elements are dropped until
+ the `predicate` returns falsey. The `predicate` is invoked with three arguments: ``(value,
+ index, array)``.
+
+ Args:
+ array: List to process.
+ predicate: Predicate called per iteration
+
+ Returns:
+ Dropped list.
+
+ Example:
+
+ >>> drop_right_while([1, 2, 3, 4], lambda x: x >= 3)
+ [1, 2]
+
+ .. versionadded:: 1.1.0
+ """
+ n = len(array)
+ for is_true, _, _, _ in iteriteratee(array, predicate, reverse=True):
+ if is_true:
+ n -= 1
+ else:
+ break
+
+ return array[:n]
+
+
+@t.overload
+def drop_while(
+ array: t.Sequence[T], predicate: t.Callable[[T, int, t.List[T]], t.Any]
+) -> t.List[T]: ...
+
+
+@t.overload
+def drop_while(array: t.Sequence[T], predicate: t.Callable[[T, int], t.Any]) -> t.List[T]: ...
+
+
+@t.overload
+def drop_while(array: t.Sequence[T], predicate: t.Callable[[T], t.Any]) -> t.List[T]: ...
+
+
+@t.overload
+def drop_while(array: t.Sequence[T], predicate: None = None) -> t.List[T]: ...
+
+
+def drop_while(array, predicate=None):
+ """
+ Creates a slice of `array` excluding elements dropped from the beginning. Elements are dropped
+ until the `predicate` returns falsey. The `predicate` is invoked with three arguments: ``(value,
+ index, array)``.
+
+ Args:
+ array: List to process.
+ predicate: Predicate called per iteration
+
+ Returns:
+ Dropped list.
+
+ Example:
+
+ >>> drop_while([1, 2, 3, 4], lambda x: x < 3)
+ [3, 4]
+
+ .. versionadded:: 1.1.0
+ """
+ n = 0
+ for is_true, _, _, _ in iteriteratee(array, predicate):
+ if is_true:
+ n += 1
+ else:
+ break
+
+ return array[n:]
+
+
+def duplicates(
+ array: t.Sequence[T], iteratee: t.Union[t.Callable[[T], t.Any], IterateeObjT, None] = None
+) -> t.List[T]:
+ """
+ Creates a unique list of duplicate values from `array`. If iteratee is passed, each element of
+ array is passed through an iteratee before duplicates are computed. The iteratee is invoked with
+ three arguments: ``(value, index, array)``. If an object path is passed for iteratee, the
+ created iteratee will return the path value of the given element. If an object is passed for
+ iteratee, the created filter style iteratee will return ``True`` for elements that have the
+ properties of the given object, else ``False``.
+
+ Args:
+ array: List to process.
+ iteratee: Iteratee applied per iteration.
+
+ Returns:
+ List of duplicates.
+
+ Example:
+
+ >>> duplicates([0, 1, 3, 2, 3, 1])
+ [3, 1]
+
+ .. versionadded:: 3.0.0
+ """
+ if iteratee:
+ cbk = pyd.iteratee(iteratee)
+ computed = [cbk(item) for item in array]
+ else:
+ computed = array # type: ignore
+
+ # NOTE: Using array[i] instead of item since iteratee could have modified
+ # returned item values.
+ lst = uniq(array[i] for i, _ in iterduplicates(computed))
+
+ return lst
+
+
+def fill(
+ array: t.Sequence[T], value: T2, start: int = 0, end: t.Union[int, None] = None
+) -> t.List[t.Union[T, T2]]:
+ """
+ Fills elements of array with value from `start` up to, but not including, `end`.
+
+ Args:
+ array: List to fill.
+ value: Value to fill with.
+ start: Index to start filling. Defaults to ``0``.
+ end: Index to end filling. Defaults to ``len(array)``.
+
+ Returns:
+ Filled `array`.
+
+ Example:
+
+ >>> fill([1, 2, 3, 4, 5], 0)
+ [0, 0, 0, 0, 0]
+ >>> fill([1, 2, 3, 4, 5], 0, 1, 3)
+ [1, 0, 0, 4, 5]
+ >>> fill([1, 2, 3, 4, 5], 0, 0, 100)
+ [0, 0, 0, 0, 0]
+
+ Warning:
+ `array` is modified in place.
+
+ .. versionadded:: 3.1.0
+ """
+ if end is None:
+ end = len(array)
+ else:
+ end = min(end, len(array))
+
+ # Use this style of assignment so that `array` is mutated.
+ array[:] = array[:start] + [value] * len(array[start:end]) + array[end:] # type: ignore
+ return array # type: ignore
+
+
+@t.overload
+def find_index(array: t.Iterable[T], predicate: t.Callable[[T, int, t.List[T]], t.Any]) -> int: ...
+
+
+@t.overload
+def find_index(array: t.Iterable[T], predicate: t.Callable[[T, int], t.Any]) -> int: ...
+
+
+@t.overload
+def find_index(array: t.Iterable[T], predicate: t.Callable[[T], t.Any]) -> int: ...
+
+
+@t.overload
+def find_index(array: t.Iterable[t.Any], predicate: IterateeObjT) -> int: ...
+
+
+@t.overload
+def find_index(array: t.Iterable[t.Any], predicate: None = None) -> int: ...
+
+
+def find_index(array, predicate=None):
+ """
+ This method is similar to :func:`pydash.collections.find`, except that it returns the index of
+ the element that passes the predicate check, instead of the element itself.
+
+ Args:
+ array: List to process.
+ predicate: Predicate applied per iteration.
+
+ Returns:
+ Index of found item or ``-1`` if not found.
+
+ Example:
+
+ >>> find_index([1, 2, 3, 4], lambda x: x >= 3)
+ 2
+ >>> find_index([1, 2, 3, 4], lambda x: x > 4)
+ -1
+
+ .. versionadded:: 1.0.0
+ """
+ search = (i for is_true, _, i, _ in iteriteratee(array, predicate) if is_true)
+ return next(search, -1)
+
+
+@t.overload
+def find_last_index(
+ array: t.Iterable[T], predicate: t.Callable[[T, int, t.List[T]], t.Any]
+) -> int: ...
+
+
+@t.overload
+def find_last_index(array: t.Iterable[T], predicate: t.Callable[[T, int], t.Any]) -> int: ...
+
+
+@t.overload
+def find_last_index(array: t.Iterable[T], predicate: t.Callable[[T], t.Any]) -> int: ...
+
+
+@t.overload
+def find_last_index(array: t.Iterable[t.Any], predicate: IterateeObjT) -> int: ...
+
+
+@t.overload
+def find_last_index(array: t.Iterable[t.Any], predicate: None = None) -> int: ...
+
+
+def find_last_index(array, predicate=None):
+ """
+ This method is similar to :func:`find_index`, except that it iterates over elements from right
+ to left.
+
+ Args:
+ array: List to process.
+ predicate: Predicate applied per iteration.
+
+ Returns:
+ Index of found item or ``-1`` if not found.
+
+ Example:
+
+ >>> find_last_index([1, 2, 3, 4], lambda x: x >= 3)
+ 3
+ >>> find_last_index([1, 2, 3, 4], lambda x: x > 4)
+ -1
+
+ .. versionadded:: 1.0.0
+ """
+ search = (i for is_true, _, i, _ in iteriteratee(array, predicate, reverse=True) if is_true)
+ return next(search, -1)
+
+
+@t.overload
+def flatten(array: t.Iterable[t.Iterable[T]]) -> t.List[T]: ...
+
+
+@t.overload
+def flatten(array: t.Iterable[T]) -> t.List[T]: ...
+
+
+def flatten(array):
+ """
+ Flattens array a single level deep.
+
+ Args:
+ array: List to flatten.
+
+ Returns:
+ Flattened list.
+
+ Example:
+
+ >>> flatten([[1], [2, [3]], [[4]]])
+ [1, 2, [3], [4]]
+
+
+ .. versionadded:: 1.0.0
+
+ .. versionchanged:: 2.0.0
+ Removed `callback` option. Added ``is_deep`` option. Made it shallow
+ by default.
+
+ .. versionchanged:: 4.0.0
+ Removed ``is_deep`` option. Use :func:`flatten_deep` instead.
+ """
+ return flatten_depth(array, depth=1)
+
+
+def flatten_deep(array: t.Iterable[t.Any]) -> t.List[t.Any]:
+ """
+ Flattens an array recursively.
+
+ Args:
+ array: List to flatten.
+
+ Returns:
+ Flattened list.
+
+ Example:
+
+ >>> flatten_deep([[1], [2, [3]], [[4]]])
+ [1, 2, 3, 4]
+
+ .. versionadded:: 2.0.0
+ """
+ return flatten_depth(array, depth=-1)
+
+
+def flatten_depth(array: t.Iterable[t.Any], depth: int = 1) -> t.List[t.Any]:
+ """
+ Recursively flatten `array` up to `depth` times.
+
+ Args:
+ array: List to flatten.
+ depth: Depth to flatten to. Defaults to ``1``.
+
+ Returns:
+ Flattened list.
+
+ Example:
+
+ >>> flatten_depth([[[1], [2, [3]], [[4]]]], 1)
+ [[1], [2, [3]], [[4]]]
+ >>> flatten_depth([[[1], [2, [3]], [[4]]]], 2)
+ [1, 2, [3], [4]]
+ >>> flatten_depth([[[1], [2, [3]], [[4]]]], 3)
+ [1, 2, 3, 4]
+ >>> flatten_depth([[[1], [2, [3]], [[4]]]], 4)
+ [1, 2, 3, 4]
+
+ .. versionadded:: 4.0.0
+ """
+ return list(iterflatten(array, depth=depth))
+
+
+@t.overload
+def from_pairs(pairs: t.Iterable[t.Tuple[T, T2]]) -> t.Dict[T, T2]: ...
+
+
+@t.overload
+def from_pairs(
+ pairs: t.Iterable[t.List[t.Union[T, T2]]],
+) -> t.Dict[t.Union[T, T2], t.Union[T, T2]]: ...
+
+
+def from_pairs(pairs):
+ """
+ Returns a dict from the given list of pairs.
+
+ Args:
+ pairs: List of key-value pairs.
+
+ Returns:
+ dict
+
+ Example:
+
+ >>> from_pairs([["a", 1], ["b", 2]]) == {"a": 1, "b": 2}
+ True
+
+ .. versionadded:: 4.0.0
+ """
+ return dict(pairs)
+
+
+def head(array: t.Sequence[T]) -> t.Union[T, None]:
+ """
+ Return the first element of `array`.
+
+ Args:
+ array: List to process.
+
+ Returns:
+ First element of list.
+
+ Example:
+
+ >>> head([1, 2, 3, 4])
+ 1
+
+ .. versionadded:: 1.0.0
+
+ .. versionchanged::
+ Renamed from ``first`` to ``head``.
+ """
+ return base_get(array, 0, default=None)
+
+
+def index_of(array: t.Sequence[T], value: T, from_index: int = 0) -> int:
+ """
+ Gets the index at which the first occurrence of value is found.
+
+ Args:
+ array: List to search.
+ value: Value to search for.
+ from_index: Index to search from.
+
+ Returns:
+ Index of found item or ``-1`` if not found.
+
+ Example:
+
+ >>> index_of([1, 2, 3, 4], 2)
+ 1
+ >>> index_of([2, 1, 2, 3], 2, from_index=1)
+ 2
+
+ .. versionadded:: 1.0.0
+ """
+ try:
+ return array.index(value, from_index)
+ except ValueError:
+ return -1
+
+
+def initial(array: t.Sequence[T]) -> t.Sequence[T]:
+ """
+ Return all but the last element of `array`.
+
+ Args:
+ array: List to process.
+
+ Returns:
+ Initial part of `array`.
+
+ Example:
+
+ >>> initial([1, 2, 3, 4])
+ [1, 2, 3]
+
+ .. versionadded:: 1.0.0
+ """
+ return array[:-1]
+
+
+@t.overload
+def intercalate(array: t.Iterable[t.Iterable[T]], separator: T2) -> t.List[t.Union[T, T2]]: ...
+
+
+@t.overload
+def intercalate(array: t.Iterable[T], separator: T2) -> t.List[t.Union[T, T2]]: ...
+
+
+def intercalate(array, separator):
+ """
+ Like :func:`intersperse` for lists of lists but shallowly flattening the result.
+
+ Args:
+ array: List to intercalate.
+ separator: Element to insert.
+
+ Returns:
+ Intercalated list.
+
+ Example:
+
+ >>> intercalate([1, [2], [3], 4], "x")
+ [1, 'x', 2, 'x', 3, 'x', 4]
+
+
+ .. versionadded:: 2.0.0
+ """
+ return flatten(intersperse(array, separator))
+
+
+def interleave(*arrays: t.Iterable[T]) -> t.List[T]:
+ """
+ Merge multiple lists into a single list by inserting the next element of each list by sequential
+ round-robin into the new list.
+
+ Args:
+ arrays: Lists to interleave.
+
+ Returns:
+ Interleaved list.
+
+ Example:
+
+ >>> interleave([1, 2, 3], [4, 5, 6], [7, 8, 9])
+ [1, 4, 7, 2, 5, 8, 3, 6, 9]
+
+ .. versionadded:: 2.0.0
+ """
+ return list(iterinterleave(*arrays))
+
+
+def intersection(array: t.Sequence[T], *others: t.Iterable[t.Any]) -> t.List[T]:
+ """
+ Computes the intersection of all the passed-in arrays.
+
+ Args:
+ array: The array to find the intersection of.
+ others: Lists to check for intersection with `array`.
+
+ Returns:
+ Intersection of provided lists.
+
+ Example:
+
+ >>> intersection([1, 2, 3], [1, 2, 3, 4, 5], [2, 3])
+ [2, 3]
+
+ >>> intersection([1, 2, 3])
+ [1, 2, 3]
+
+ .. versionadded:: 1.0.0
+
+ .. versionchanged:: 4.0.0
+ Support finding intersection of unhashable types.
+ """
+ return intersection_with(array, *others)
+
+
+@t.overload
+def intersection_by(
+ array: t.Sequence[T],
+ *others: t.Iterable[t.Any],
+ iteratee: t.Union[t.Callable[[T], t.Any], IterateeObjT],
+) -> t.List[T]: ...
+
+
+@t.overload
+def intersection_by(
+ array: t.Sequence[T], *others: t.Union[t.Iterable[t.Any], t.Callable[[T], t.Any], IterateeObjT]
+) -> t.List[T]: ...
+
+
+def intersection_by(array, *others, **kwargs):
+ """
+ This method is like :func:`intersection` except that it accepts an iteratee which is invoked for
+ each element of each array to generate the criterion by which they're compared. The order and
+ references of result values are determined by `array`. The iteratee is invoked with one
+ argument: ``(value)``.
+
+ Args:
+ array: The array to find the intersection of.
+ others: Lists to check for intersection with `array`.
+
+ Keyword Args:
+ iteratee: Function to transform the elements of the arrays. Defaults to
+ :func:`.identity`.
+
+ Returns:
+ Intersection of provided lists.
+
+ Example:
+
+ >>> intersection_by([1.2, 1.5, 1.7, 2.8], [0.9, 3.2], round)
+ [1.2, 2.8]
+
+ .. versionadded:: 4.0.0
+ """
+ array = array[:]
+
+ if not others:
+ return array
+
+ iteratee, others = parse_iteratee("iteratee", *others, **kwargs)
+
+ # Sort by smallest list length to make intersection faster.
+ others = sorted(others, key=lambda other: len(other))
+
+ for other in others:
+ array = list(iterintersection(array, other, iteratee=iteratee))
+ if not array:
+ break
+
+ return array
+
+
+@t.overload
+def intersection_with(
+ array: t.Sequence[T], *others: t.Iterable[T2], comparator: t.Callable[[T, T2], t.Any]
+) -> t.List[T]: ...
+
+
+@t.overload
+def intersection_with(
+ array: t.Sequence[T], *others: t.Union[t.Iterable[T2], t.Callable[[T, T2], t.Any]]
+) -> t.List[T]: ...
+
+
+def intersection_with(array, *others, **kwargs):
+ """
+ This method is like :func:`intersection` except that it accepts a comparator which is invoked to
+ compare the elements of all arrays. The order and references of result values are determined by
+ the first array. The comparator is invoked with two arguments: ``(arr_val, oth_val)``.
+
+ Args:
+ array: The array to find the intersection of.
+ others: Lists to check for intersection with `array`.
+
+ Keyword Args:
+ comparator: Function to compare the elements of the arrays. Defaults to
+ :func:`.is_equal`.
+
+ Returns:
+ Intersection of provided lists.
+
+ Example:
+
+ >>> array = ["apple", "banana", "pear"]
+ >>> others = (["avocado", "pumpkin"], ["peach"])
+ >>> comparator = lambda a, b: a[0] == b[0]
+ >>> intersection_with(array, *others, comparator=comparator)
+ ['pear']
+
+ .. versionadded:: 4.0.0
+ """
+ array = array[:]
+
+ if not others:
+ return array
+
+ comparator, others = parse_iteratee("comparator", *others, **kwargs)
+
+ # Sort by smallest list length to reduce to intersection faster.
+ others = sorted(others, key=lambda other: len(other))
+
+ for other in others:
+ array = list(iterintersection(array, other, comparator=comparator))
+ if not array:
+ break
+
+ return array
+
+
+def intersperse(array: t.Iterable[T], separator: T2) -> t.List[t.Union[T, T2]]:
+ """
+ Insert a separating element between the elements of `array`.
+
+ Args:
+ array: List to intersperse.
+ separator: Element to insert.
+
+ Returns:
+ Interspersed list.
+
+ Example:
+
+ >>> intersperse([1, [2], [3], 4], "x")
+ [1, 'x', [2], 'x', [3], 'x', 4]
+
+ .. versionadded:: 2.0.0
+ """
+ return list(iterintersperse(array, separator))
+
+
+def last(array: t.Sequence[T]) -> t.Union[T, None]:
+ """
+ Return the last element of `array`.
+
+ Args:
+ array: List to process.
+
+ Returns:
+ Last part of `array`.
+
+ Example:
+
+ >>> last([1, 2, 3, 4])
+ 4
+
+ .. versionadded:: 1.0.0
+ """
+ return base_get(array, -1, default=None)
+
+
+def last_index_of(
+ array: t.Sequence[t.Any], value: t.Any, from_index: t.Union[int, None] = None
+) -> int:
+ """
+ Gets the index at which the last occurrence of value is found.
+
+ Args:
+ array: List to search.
+ value: Value to search for.
+ from_index: Index to search from.
+
+ Returns:
+ Index of found item or ``-1`` if not found.
+
+ Example:
+
+ >>> last_index_of([1, 2, 2, 4], 2)
+ 2
+ >>> last_index_of([1, 2, 2, 4], 2, from_index=1)
+ 1
+
+ .. versionadded:: 1.0.0
+ """
+ index = array_len = len(array)
+
+ try:
+ # safe as we are catching any type errors
+ from_index = int(from_index) # type: ignore
+ except (TypeError, ValueError):
+ pass
+ else:
+ # Set starting index base on from_index offset.
+ index = max(0, index + from_index) if from_index < 0 else min(from_index, index - 1)
+
+ while index:
+ if index < array_len and array[index] == value:
+ return index
+ index -= 1
+ return -1
+
+
+@t.overload
+def mapcat(
+ array: t.Iterable[T],
+ iteratee: t.Callable[[T, int, t.List[T]], t.Union[t.List[T2], t.List[t.List[T2]]]],
+) -> t.List[T2]: ...
+
+
+@t.overload
+def mapcat(array: t.Iterable[T], iteratee: t.Callable[[T, int, t.List[T]], T2]) -> t.List[T2]: ...
+
+
+@t.overload
+def mapcat(
+ array: t.Iterable[T], iteratee: t.Callable[[T, int], t.Union[t.List[T2], t.List[t.List[T2]]]]
+) -> t.List[T2]: ...
+
+
+@t.overload
+def mapcat(array: t.Iterable[T], iteratee: t.Callable[[T, int], T2]) -> t.List[T2]: ...
+
+
+@t.overload
+def mapcat(
+ array: t.Iterable[T], iteratee: t.Callable[[T], t.Union[t.List[T2], t.List[t.List[T2]]]]
+) -> t.List[T2]: ...
+
+
+@t.overload
+def mapcat(array: t.Iterable[T], iteratee: t.Callable[[T], T2]) -> t.List[T2]: ...
+
+
+@t.overload
+def mapcat(
+ array: t.Iterable[t.Union[t.List[T], t.List[t.List[T]]]], iteratee: None = None
+) -> t.List[t.Union[T, t.List[T]]]: ...
+
+
+def mapcat(array, iteratee=None):
+ """
+ Map an iteratee to each element of a list and concatenate the results into a single list using
+ :func:`concat`.
+
+ Args:
+ array: List to map and concatenate.
+ iteratee: Iteratee to apply to each element.
+
+ Returns:
+ Mapped and concatenated list.
+
+ Example:
+
+ >>> mapcat(range(4), lambda x: list(range(x)))
+ [0, 0, 1, 0, 1, 2]
+
+ .. versionadded:: 2.0.0
+ """
+ return concat(*pyd.map_(array, iteratee))
+
+
+def nth(array: t.Iterable[T], pos: int = 0) -> t.Union[T, None]:
+ """
+ Gets the element at index n of array.
+
+ Args:
+ array: List passed in by the user.
+ pos: Index of element to return.
+
+ Returns:
+ Returns the element at :attr:`pos`.
+
+ Example:
+
+ >>> nth([1, 2, 3], 0)
+ 1
+ >>> nth([3, 4, 5, 6], 2)
+ 5
+ >>> nth([11, 22, 33], -1)
+ 33
+ >>> nth([11, 22, 33])
+ 11
+
+ .. versionadded:: 4.0.0
+ """
+ return pyd.get(array, pos)
+
+
+def pop(array: t.List[T], index: int = -1) -> T:
+ """
+ Remove element of array at `index` and return element.
+
+ Args:
+ array: List to pop from.
+ index: Index to remove element from. Defaults to ``-1``.
+
+ Returns:
+ Value at `index`.
+
+ Warning:
+ `array` is modified in place.
+
+ Example:
+
+ >>> array = [1, 2, 3, 4]
+ >>> item = pop(array)
+ >>> item
+ 4
+ >>> array
+ [1, 2, 3]
+ >>> item = pop(array, index=0)
+ >>> item
+ 1
+ >>> array
+ [2, 3]
+
+ .. versionadded:: 2.2.0
+ """
+ return array.pop(index)
+
+
+def pull(array: t.List[T], *values: T) -> t.List[T]:
+ """
+ Removes all provided values from the given array.
+
+ Args:
+ array: List to pull from.
+ values: Values to remove.
+
+ Returns:
+ Modified `array`.
+
+ Warning:
+ `array` is modified in place.
+
+ Example:
+
+ >>> pull([1, 2, 2, 3, 3, 4], 2, 3)
+ [1, 4]
+
+ .. versionadded:: 1.0.0
+
+ .. versionchanged:: 4.0.0
+ :func:`pull` method now calls :func:`pull_all` method for the desired
+ functionality.
+ """
+ return pull_all(array, values)
+
+
+def pull_all(array: t.List[T], values: t.Iterable[T]) -> t.List[T]:
+ """
+ Removes all provided values from the given array.
+
+ Args:
+ array: Array to modify.
+ values: Values to remove.
+
+ Returns:
+ Modified `array`.
+
+ Example:
+
+ >>> pull_all([1, 2, 2, 3, 3, 4], [2, 3])
+ [1, 4]
+
+ .. versionadded:: 4.0.0
+ """
+ # Use this style of assignment so that `array` is mutated.
+ array[:] = without(array, *values)
+ return array
+
+
+def pull_all_by(
+ array: t.List[T],
+ values: t.Iterable[T],
+ iteratee: t.Union[IterateeObjT, t.Callable[[T], t.Any], None] = None,
+) -> t.List[T]:
+ """
+ This method is like :func:`pull_all` except that it accepts iteratee which is invoked for each
+ element of array and values to generate the criterion by which they're compared. The iteratee is
+ invoked with one argument: ``(value)``.
+
+ Args:
+ array: Array to modify.
+ values: Values to remove.
+ iteratee: Function to transform the elements of the arrays. Defaults to
+ :func:`.identity`.
+
+ Returns:
+ Modified `array`.
+
+ Example:
+
+ >>> array = [{"x": 1}, {"x": 2}, {"x": 3}, {"x": 1}]
+ >>> pull_all_by(array, [{"x": 1}, {"x": 3}], "x")
+ [{'x': 2}]
+
+ .. versionadded:: 4.0.0
+ """
+ values = difference(array, difference_by(array, values, iteratee=iteratee))
+ return pull_all(array, values)
+
+
+def pull_all_with(
+ array: t.List[T],
+ values: t.Iterable[T],
+ comparator: t.Union[t.Callable[[T, T], t.Any], None] = None,
+) -> t.List[T]:
+ """
+ This method is like :func:`pull_all` except that it accepts comparator which is invoked to
+ compare elements of array to values. The comparator is invoked with two arguments: ``(arr_val,
+ oth_val)``.
+
+ Args:
+ array: Array to modify.
+ values: Values to remove.
+ comparator: Function to compare the elements of the arrays. Defaults to
+ :func:`.is_equal`.
+
+ Returns:
+ Modified `array`.
+
+ Example:
+
+ >>> array = [{"x": 1, "y": 2}, {"x": 3, "y": 4}, {"x": 5, "y": 6}]
+ >>> res = pull_all_with(array, [{"x": 3, "y": 4}], lambda a, b: a == b)
+ >>> res == [{"x": 1, "y": 2}, {"x": 5, "y": 6}]
+ True
+ >>> array = [{"x": 1, "y": 2}, {"x": 3, "y": 4}, {"x": 5, "y": 6}]
+ >>> res = pull_all_with(array, [{"x": 3, "y": 4}], lambda a, b: a != b)
+ >>> res == [{"x": 3, "y": 4}]
+ True
+
+ .. versionadded:: 4.0.0
+ """
+ values = difference(array, difference_with(array, values, comparator=comparator))
+ return pull_all(array, values)
+
+
+def pull_at(array: t.List[T], *indexes: int) -> t.List[T]:
+ """
+ Removes elements from `array` corresponding to the specified indexes and returns a list of the
+ removed elements. Indexes may be specified as a list of indexes or as individual arguments.
+
+ Args:
+ array: List to pull from.
+ indexes: Indexes to pull.
+
+ Returns:
+ Modified `array`.
+
+ Warning:
+ `array` is modified in place.
+
+ Example:
+
+ >>> pull_at([1, 2, 3, 4], 0, 2)
+ [2, 4]
+
+ .. versionadded:: 1.1.0
+ """
+ flat_indexes = flatten(indexes)
+ for index in sorted(flat_indexes, reverse=True):
+ del array[index]
+
+ return array
+
+
+def push(array: t.List[T], *items: T2) -> t.List[t.Union[T, T2]]:
+ """
+ Push items onto the end of `array` and return modified `array`.
+
+ Args:
+ array: List to push to.
+ items: Items to append.
+
+ Returns:
+ Modified `array`.
+
+ Warning:
+ `array` is modified in place.
+
+ Example:
+
+ >>> array = [1, 2, 3]
+ >>> push(array, 4, 5, [6])
+ [1, 2, 3, 4, 5, [6]]
+
+ .. versionadded:: 2.2.0
+
+ .. versionchanged:: 4.0.0
+ Removed alias ``append``.
+ """
+ for item in items:
+ array.append(item) # type: ignore
+ return array # type: ignore
+
+
+def remove(
+ array: t.List[T],
+ predicate: t.Union[
+ t.Callable[[T, int, t.List[T]], t.Any],
+ t.Callable[[T, int], t.Any],
+ t.Callable[[T], t.Any],
+ None,
+ ] = None,
+) -> t.List[T]:
+ """
+ Removes all elements from a list that the predicate returns truthy for and returns an array of
+ removed elements.
+
+ Args:
+ array: List to remove elements from.
+ predicate: Predicate applied per iteration.
+
+ Returns:
+ Removed elements of `array`.
+
+ Warning:
+ `array` is modified in place.
+
+ Example:
+
+ >>> array = [1, 2, 3, 4]
+ >>> items = remove(array, lambda x: x >= 3)
+ >>> items
+ [3, 4]
+ >>> array
+ [1, 2]
+
+ .. versionadded:: 1.0.0
+ """
+ removed = []
+ kept = []
+
+ for is_true, _, i, _ in iteriteratee(array, predicate):
+ if is_true:
+ removed.append(array[i])
+ else:
+ kept.append(array[i])
+
+ # Modify array in place.
+ array[:] = kept
+
+ return removed
+
+
+def reverse(array: SequenceT) -> SequenceT:
+ """
+ Return `array` in reverse order.
+
+ Args:
+ array: Object to process.
+
+ Returns:
+ Reverse of object.
+
+ Example:
+
+ >>> reverse([1, 2, 3, 4])
+ [4, 3, 2, 1]
+
+ .. versionadded:: 2.2.0
+ """
+ # NOTE: Using this method to reverse object since it works for both lists and strings.
+ return array[::-1] # type: ignore
+
+
+def shift(array: t.List[T]) -> T:
+ """
+ Remove the first element of `array` and return it.
+
+ Args:
+ array: List to shift.
+
+ Returns:
+ First element of `array`.
+
+ Warning:
+ `array` is modified in place.
+
+ Example:
+
+ >>> array = [1, 2, 3, 4]
+ >>> item = shift(array)
+ >>> item
+ 1
+ >>> array
+ [2, 3, 4]
+
+ .. versionadded:: 2.2.0
+ """
+ return pop(array, 0)
+
+
+def slice_(array: SequenceT, start: int = 0, end: t.Union[int, None] = None) -> SequenceT:
+ """
+ Slices `array` from the `start` index up to, but not including, the `end` index.
+
+ Args:
+ array: Array to slice.
+ start: Start index. Defaults to ``0``.
+ end: End index. Defaults to selecting the value at ``start`` index.
+
+ Returns:
+ Sliced list.
+
+ Example:
+
+ >>> slice_([1, 2, 3, 4])
+ [1]
+ >>> slice_([1, 2, 3, 4], 1)
+ [2]
+ >>> slice_([1, 2, 3, 4], 1, 3)
+ [2, 3]
+
+ .. versionadded:: 1.1.0
+ """
+ if end is None:
+ end = (start + 1) if start >= 0 else (len(array) + start + 1)
+
+ return array[start:end] # type: ignore
+
+
+@t.overload
+def sort(
+ array: t.List["SupportsRichComparisonT"],
+ comparator: None = None,
+ key: None = None,
+ reverse: bool = False,
+) -> t.List["SupportsRichComparisonT"]: ...
+
+
+@t.overload
+def sort(
+ array: t.List[T], comparator: t.Callable[[T, T], int], *, reverse: bool = False
+) -> t.List[T]: ...
+
+
+@t.overload
+def sort(
+ array: t.List[T], *, key: t.Callable[[T], "SupportsRichComparisonT"], reverse: bool = False
+) -> t.List[T]: ...
+
+
+def sort(array, comparator=None, key=None, reverse=False):
+ """
+ Sort `array` using optional `comparator`, `key`, and `reverse` options and return sorted
+ `array`.
+
+ Note:
+ Python 3 removed the option to pass a custom comparator function and instead only allows a
+ key function. Therefore, if a comparator function is passed in, it will be converted to a
+ key function automatically using ``functools.cmp_to_key``.
+
+ Args:
+ array: List to sort.
+ comparator: A custom comparator function used to sort the list.
+ Function should accept two arguments and return a negative, zero, or position number
+ depending on whether the first argument is considered smaller than, equal to, or larger
+ than the second argument. Defaults to ``None``. This argument is mutually exclusive with
+ `key`.
+ key: A function of one argument used to extract a comparator key from each list element.
+ Defaults to ``None``. This argument is mutually exclusive with `comparator`.
+ reverse: Whether to reverse the sort. Defaults to ``False``.
+
+ Returns:
+ Sorted list.
+
+ Warning:
+ `array` is modified in place.
+
+ Example:
+
+ >>> sort([2, 1, 4, 3])
+ [1, 2, 3, 4]
+ >>> sort([2, 1, 4, 3], reverse=True)
+ [4, 3, 2, 1]
+ >>> results = sort([{'a': 2, 'b': 1},\
+ {'a': 3, 'b': 2},\
+ {'a': 0, 'b': 3}],\
+ key=lambda item: item['a'])
+ >>> assert results == [{'a': 0, 'b': 3},\
+ {'a': 2, 'b': 1},\
+ {'a': 3, 'b': 2}]
+
+ .. versionadded:: 2.2.0
+ """
+ if comparator and key:
+ raise ValueError('The "comparator" and "key" arguments are mutually exclusive')
+
+ if comparator:
+ key = cmp_to_key(comparator)
+
+ array.sort(key=key, reverse=reverse)
+ return array
+
+
+def sorted_index(
+ array: t.Sequence["SupportsRichComparisonT"], value: "SupportsRichComparisonT"
+) -> int:
+ """
+ Uses a binary search to determine the lowest index at which `value` should be inserted into
+ `array` in order to maintain its sort order.
+
+ Args:
+ array: List to inspect.
+ value: Value to evaluate.
+
+ Returns:
+ Returns the index at which `value` should be inserted into `array`.
+
+ Example:
+
+ >>> sorted_index([1, 2, 2, 3, 4], 2)
+ 1
+
+ .. versionadded:: 1.0.0
+
+ .. versionchanged:: 4.0.0
+ Move iteratee support to :func:`sorted_index_by`.
+ """
+ return sorted_index_by(array, value)
+
+
+@t.overload
+def sorted_index_by(
+ array: t.Sequence[T],
+ value: T,
+ iteratee: t.Union[IterateeObjT, t.Callable[[T], "SupportsRichComparisonT"]],
+) -> int: ...
+
+
+@t.overload
+def sorted_index_by(
+ array: t.Sequence["SupportsRichComparisonT"],
+ value: "SupportsRichComparisonT",
+ iteratee: None = None,
+) -> int: ...
+
+
+def sorted_index_by(array, value, iteratee=None):
+ """
+ This method is like :func:`sorted_index` except that it accepts iteratee which is invoked for
+ `value` and each element of `array` to compute their sort ranking. The iteratee is invoked with
+ one argument: ``(value)``.
+
+ Args:
+ array: List to inspect.
+ value: Value to evaluate.
+ iteratee: The iteratee invoked per element. Defaults to :func:`.identity`.
+
+ Returns:
+ Returns the index at which `value` should be inserted into `array`.
+
+ Example:
+
+ >>> array = [{"x": 4}, {"x": 5}]
+ >>> sorted_index_by(array, {"x": 4}, lambda o: o["x"])
+ 0
+ >>> sorted_index_by(array, {"x": 4}, "x")
+ 0
+
+ .. versionadded:: 4.0.0
+ """
+ if iteratee:
+ # Generate array of sorted keys computed using iteratee.
+ iteratee = pyd.iteratee(iteratee)
+ array = sorted(iteratee(item) for item in array)
+ value = iteratee(value)
+
+ return bisect_left(array, value)
+
+
+def sorted_index_of(
+ array: t.Sequence["SupportsRichComparisonT"], value: "SupportsRichComparisonT"
+) -> int:
+ """
+ Returns the index of the matched `value` from the sorted `array`, else ``-1``.
+
+ Args:
+ array: Array to inspect.
+ value: Value to search for.
+
+ Returns:
+ Returns the index of the first matched value, else ``-1``.
+
+ Example:
+
+ >>> sorted_index_of([3, 5, 7, 10], 3)
+ 0
+ >>> sorted_index_of([10, 10, 5, 7, 3], 10)
+ -1
+
+ .. versionadded:: 4.0.0
+ """
+ index = sorted_index(array, value)
+
+ if index < len(array) and array[index] == value:
+ return index
+ else:
+ return -1
+
+
+def sorted_last_index(
+ array: t.Sequence["SupportsRichComparisonT"], value: "SupportsRichComparisonT"
+) -> int:
+ """
+ This method is like :func:`sorted_index` except that it returns the highest index at which
+ `value` should be inserted into `array` in order to maintain its sort order.
+
+ Args:
+ array: List to inspect.
+ value: Value to evaluate.
+
+ Returns:
+ Returns the index at which `value` should be inserted into `array`.
+
+ Example:
+
+ >>> sorted_last_index([1, 2, 2, 3, 4], 2)
+ 3
+
+ .. versionadded:: 1.1.0
+
+ .. versionchanged:: 4.0.0
+ Move iteratee support to :func:`sorted_last_index_by`.
+ """
+ return sorted_last_index_by(array, value)
+
+
+@t.overload
+def sorted_last_index_by(
+ array: t.Sequence[T],
+ value: T,
+ iteratee: t.Union[IterateeObjT, t.Callable[[T], "SupportsRichComparisonT"]],
+) -> int: ...
+
+
+@t.overload
+def sorted_last_index_by(
+ array: t.Sequence["SupportsRichComparisonT"],
+ value: "SupportsRichComparisonT",
+ iteratee: None = None,
+) -> int: ...
+
+
+def sorted_last_index_by(array, value, iteratee=None):
+ """
+ This method is like :func:`sorted_last_index` except that it accepts iteratee which is invoked
+ for `value` and each element of `array` to compute their sort ranking. The iteratee is invoked
+ with one argument: ``(value)``.
+
+ Args:
+ array: List to inspect.
+ value: Value to evaluate.
+ iteratee: The iteratee invoked per element. Defaults to :func:`.identity`.
+
+ Returns:
+ Returns the index at which `value` should be inserted into `array`.
+
+ Example:
+
+ >>> array = [{"x": 4}, {"x": 5}]
+ >>> sorted_last_index_by(array, {"x": 4}, lambda o: o["x"])
+ 1
+ >>> sorted_last_index_by(array, {"x": 4}, "x")
+ 1
+ """
+ if iteratee:
+ # Generate array of sorted keys computed using iteratee.
+ iteratee = pyd.iteratee(iteratee)
+ array = sorted(iteratee(item) for item in array)
+ value = iteratee(value)
+
+ return bisect_right(array, value)
+
+
+def sorted_last_index_of(
+ array: t.Sequence["SupportsRichComparisonT"], value: "SupportsRichComparisonT"
+) -> int:
+ """
+ This method is like :func:`last_index_of` except that it performs a binary search on a sorted
+ `array`.
+
+ Args:
+ array: Array to inspect.
+ value: Value to search for.
+
+ Returns:
+ Returns the index of the matched value, else ``-1``.
+
+ Example:
+
+ >>> sorted_last_index_of([4, 5, 5, 5, 6], 5)
+ 3
+ >>> sorted_last_index_of([6, 5, 5, 5, 4], 6)
+ -1
+
+ .. versionadded:: 4.0.0
+ """
+ index = sorted_last_index(array, value) - 1
+
+ if index < len(array) and array[index] == value:
+ return index
+ else:
+ return -1
+
+
+def sorted_uniq(array: t.Iterable["SupportsRichComparisonT"]) -> t.List["SupportsRichComparisonT"]:
+ """
+ Return sorted array with unique elements.
+
+ Args:
+ array: List of values to be sorted.
+
+ Returns:
+ List of unique elements in a sorted fashion.
+
+ Example:
+
+ >>> sorted_uniq([4, 2, 2, 5])
+ [2, 4, 5]
+ >>> sorted_uniq([-2, -2, 4, 1])
+ [-2, 1, 4]
+
+ .. versionadded:: 4.0.0
+ """
+ return sorted(uniq(array))
+
+
+def sorted_uniq_by(
+ array: t.Iterable["SupportsRichComparisonT"],
+ iteratee: t.Union[
+ t.Callable[["SupportsRichComparisonT"], "SupportsRichComparisonT"], None
+ ] = None,
+) -> t.List["SupportsRichComparisonT"]:
+ """
+ This method is like :func:`sorted_uniq` except that it accepts iteratee which is invoked for
+ each element in array to generate the criterion by which uniqueness is computed. The order of
+ result values is determined by the order they occur in the array. The iteratee is invoked with
+ one argument: ``(value)``.
+
+ Args:
+ array: List of values to be sorted.
+ iteratee: Function to transform the elements of the arrays. Defaults to
+ :func:`.identity`.
+
+ Returns:
+ Unique list.
+
+ Example:
+
+ >>> sorted_uniq_by([3, 2, 1, 3, 2, 1], lambda val: val % 2)
+ [2, 3]
+
+ .. versionadded:: 4.0.0
+ """
+ return sorted(uniq_by(array, iteratee=iteratee))
+
+
+def splice(
+ array: MutableSequenceT, start: int, count: t.Union[int, None] = None, *items: t.Any
+) -> MutableSequenceT:
+ """
+ Modify the contents of `array` by inserting elements starting at index `start` and removing
+ `count` number of elements after.
+
+ Args:
+ array: List to splice.
+ start: Start to splice at.
+ count: Number of items to remove starting at `start`. If ``None`` then all
+ items after `start` are removed. Defaults to ``None``.
+ items: Elements to insert starting at `start`. Each item is inserted in the order
+ given.
+
+ Returns:
+ The removed elements of `array` or the spliced string.
+
+ Warning:
+ `array` is modified in place if ``list``.
+
+ Example:
+
+ >>> array = [1, 2, 3, 4]
+ >>> splice(array, 1)
+ [2, 3, 4]
+ >>> array
+ [1]
+ >>> array = [1, 2, 3, 4]
+ >>> splice(array, 1, 2)
+ [2, 3]
+ >>> array
+ [1, 4]
+ >>> array = [1, 2, 3, 4]
+ >>> splice(array, 1, 2, 0, 0)
+ [2, 3]
+ >>> array
+ [1, 0, 0, 4]
+
+ .. versionadded:: 2.2.0
+
+ .. versionchanged:: 3.0.0
+ Support string splicing.
+ """
+ if count is None:
+ count = len(array) - start
+
+ is_string = pyd.is_string(array)
+
+ if is_string:
+ # allow reassignment with different type
+ array = list(array) # type: ignore
+
+ removed = array[start : start + count]
+ del array[start : start + count]
+
+ for item in reverse(items):
+ array.insert(start, item)
+
+ if is_string:
+ return "".join(array) # type: ignore
+ else:
+ return removed # type: ignore
+
+
+def split_at(array: t.Sequence[T], index: int) -> t.List[t.Sequence[T]]:
+ """
+ Returns a list of two lists composed of the split of `array` at `index`.
+
+ Args:
+ array: List to split.
+ index: Index to split at.
+
+ Returns:
+ Split list.
+
+ Example:
+
+ >>> split_at([1, 2, 3, 4], 2)
+ [[1, 2], [3, 4]]
+
+ .. versionadded:: 2.0.0
+ """
+ return [array[:index], array[index:]]
+
+
+def tail(array: t.Sequence[T]) -> t.Sequence[T]:
+ """
+ Return all but the first element of `array`.
+
+ Args:
+ array: List to process.
+
+ Returns:
+ Rest of the list.
+
+ Example:
+
+ >>> tail([1, 2, 3, 4])
+ [2, 3, 4]
+
+ .. versionadded:: 1.0.0
+
+ .. versionchanged:: 4.0.0
+ Renamed from ``rest`` to ``tail``.
+ """
+ return array[1:]
+
+
+def take(array: t.Sequence[T], n: int = 1) -> t.Sequence[T]:
+ """
+ Creates a slice of `array` with `n` elements taken from the beginning.
+
+ Args:
+ array: List to process.
+ n: Number of elements to take. Defaults to ``1``.
+
+ Returns:
+ Taken list.
+
+ Example:
+
+ >>> take([1, 2, 3, 4], 2)
+ [1, 2]
+
+ .. versionadded:: 1.0.0
+
+ .. versionchanged:: 1.1.0
+ Added ``n`` argument and removed as alias of :func:`first`.
+
+ .. versionchanged:: 3.0.0
+ Made ``n`` default to ``1``.
+ """
+ return take_while(array, lambda _, index: index < n)
+
+
+def take_right(array: t.Sequence[T], n: int = 1) -> t.Sequence[T]:
+ """
+ Creates a slice of `array` with `n` elements taken from the end.
+
+ Args:
+ array: List to process.
+ n: Number of elements to take. Defaults to ``1``.
+
+ Returns:
+ Taken list.
+
+ Example:
+
+ >>> take_right([1, 2, 3, 4], 2)
+ [3, 4]
+
+ .. versionadded:: 1.1.0
+
+ .. versionchanged:: 3.0.0
+ Made ``n`` default to ``1``.
+ """
+ length = len(array)
+ return take_right_while(array, lambda _, index: (length - index) <= n)
+
+
+@t.overload
+def take_right_while(
+ array: t.Sequence[T], predicate: t.Callable[[T, int, t.List[T]], t.Any]
+) -> t.Sequence[T]: ...
+
+
+@t.overload
+def take_right_while(
+ array: t.Sequence[T], predicate: t.Callable[[T, int], t.Any]
+) -> t.Sequence[T]: ...
+
+
+@t.overload
+def take_right_while(array: t.Sequence[T], predicate: t.Callable[[T], t.Any]) -> t.Sequence[T]: ...
+
+
+@t.overload
+def take_right_while(array: t.Sequence[T], predicate: None = None) -> t.Sequence[T]: ...
+
+
+def take_right_while(array, predicate=None):
+ """
+ Creates a slice of `array` with elements taken from the end. Elements are taken until the
+ `predicate` returns falsey. The `predicate` is invoked with three arguments: ``(value, index,
+ array)``.
+
+ Args:
+ array: List to process.
+ predicate: Predicate called per iteration
+
+ Returns:
+ Dropped list.
+
+ Example:
+
+ >>> take_right_while([1, 2, 3, 4], lambda x: x >= 3)
+ [3, 4]
+
+ .. versionadded:: 1.1.0
+ """
+ n = len(array)
+ for is_true, _, _, _ in iteriteratee(array, predicate, reverse=True):
+ if is_true:
+ n -= 1
+ else:
+ break
+
+ return array[n:]
+
+
+@t.overload
+def take_while(
+ array: t.Sequence[T], predicate: t.Callable[[T, int, t.List[T]], t.Any]
+) -> t.List[T]: ...
+
+
+@t.overload
+def take_while(array: t.Sequence[T], predicate: t.Callable[[T, int], t.Any]) -> t.List[T]: ...
+
+
+@t.overload
+def take_while(array: t.Sequence[T], predicate: t.Callable[[T], t.Any]) -> t.List[T]: ...
+
+
+@t.overload
+def take_while(array: t.Sequence[T], predicate: None = None) -> t.List[T]: ...
+
+
+def take_while(array, predicate=None):
+ """
+ Creates a slice of `array` with elements taken from the beginning. Elements are taken until the
+ `predicate` returns falsey. The `predicate` is invoked with three arguments: ``(value, index,
+ array)``.
+
+ Args:
+ array: List to process.
+ predicate: Predicate called per iteration
+
+ Returns:
+ Taken list.
+
+ Example:
+
+ >>> take_while([1, 2, 3, 4], lambda x: x < 3)
+ [1, 2]
+
+ .. versionadded:: 1.1.0
+ """
+ n = 0
+ for is_true, _, _, _ in iteriteratee(array, predicate):
+ if is_true:
+ n += 1
+ else:
+ break
+
+ return array[:n]
+
+
+@t.overload
+def union(array: t.Sequence[T]) -> t.List[T]: ...
+
+
+@t.overload
+def union(array: t.Sequence[T], *others: t.Sequence[T2]) -> t.List[t.Union[T, T2]]: ...
+
+
+def union(array, *others):
+ """
+ Computes the union of the passed-in arrays.
+
+ Args:
+ array: List to union with.
+ others: Lists to unionize with `array`.
+
+ Returns:
+ Unionized list.
+
+ Example:
+
+ >>> union([1, 2, 3], [2, 3, 4], [3, 4, 5])
+ [1, 2, 3, 4, 5]
+
+ .. versionadded:: 1.0.0
+ """
+ if not others:
+ return array[:]
+
+ return uniq(flatten([array] + list(others)))
+
+
+@t.overload
+def union_by(
+ array: t.Sequence[T], *others: t.Iterable[T], iteratee: t.Callable[[T], t.Any]
+) -> t.List[T]: ...
+
+
+@t.overload
+def union_by(
+ array: t.Sequence[T], *others: t.Union[t.Iterable[T], t.Callable[[T], t.Any]]
+) -> t.List[T]: ...
+
+
+def union_by(array, *others, **kwargs):
+ """
+ This method is similar to :func:`union` except that it accepts iteratee which is invoked for
+ each element of each array to generate the criterion by which uniqueness is computed.
+
+ Args:
+ array: List to unionize with.
+ others: Lists to unionize with `array`.
+
+ Keyword Args:
+ iteratee: Function to invoke on each element.
+
+ Returns:
+ Unionized list.
+
+ Example:
+
+ >>> union_by([1, 2, 3], [2, 3, 4], iteratee=lambda x: x % 2)
+ [1, 2]
+ >>> union_by([1, 2, 3], [2, 3, 4], iteratee=lambda x: x % 9)
+ [1, 2, 3, 4]
+
+ .. versionadded:: 4.0.0
+ """
+ if not others:
+ return array[:]
+
+ iteratee, others = parse_iteratee("iteratee", *others, **kwargs)
+
+ return uniq_by(flatten([array] + list(others)), iteratee=iteratee)
+
+
+@t.overload
+def union_with(
+ array: t.Sequence[T], *others: t.Iterable[T2], comparator: t.Callable[[T, T2], t.Any]
+) -> t.List[T]: ...
+
+
+@t.overload
+def union_with(
+ array: t.Sequence[T], *others: t.Union[t.Iterable[T2], t.Callable[[T, T2], t.Any]]
+) -> t.List[T]: ...
+
+
+def union_with(array, *others, **kwargs):
+ """
+ This method is like :func:`union` except that it accepts comparator which is invoked to compare
+ elements of arrays. Result values are chosen from the first array in which the value occurs.
+
+ Args:
+ array: List to unionize with.
+ others: Lists to unionize with `array`.
+
+ Keyword Args:
+ comparator: Function to compare the elements of the arrays. Defaults to
+ :func:`.is_equal`.
+
+ Returns:
+ Unionized list.
+
+ Example:
+
+ >>> comparator = lambda a, b: (a % 2) == (b % 2)
+ >>> union_with([1, 2, 3], [2, 3, 4], comparator=comparator)
+ [1, 2]
+ >>> union_with([1, 2, 3], [2, 3, 4])
+ [1, 2, 3, 4]
+
+ .. versionadded:: 4.0.0
+ """
+ if not others:
+ return array[:]
+
+ comparator, others = parse_iteratee("comparator", *others, **kwargs)
+
+ return uniq_with(flatten([array] + list(others)), comparator=comparator)
+
+
+def uniq(array: t.Iterable[T]) -> t.List[T]:
+ """
+ Creates a duplicate-value-free version of the array. If iteratee is passed, each element of
+ array is passed through an iteratee before uniqueness is computed. The iteratee is invoked with
+ three arguments: ``(value, index, array)``. If an object path is passed for iteratee, the
+ created iteratee will return the path value of the given element. If an object is passed for
+ iteratee, the created filter style iteratee will return ``True`` for elements that have the
+ properties of the given object, else ``False``.
+
+ Args:
+ array: List to process.
+
+ Returns:
+ Unique list.
+
+ Example:
+
+ >>> uniq([1, 2, 3, 1, 2, 3])
+ [1, 2, 3]
+
+ .. versionadded:: 1.0.0
+
+ .. versionchanged:: 4.0.0
+
+ - Moved `iteratee` argument to :func:`uniq_by`.
+ - Removed alias ``unique``.
+ """
+ return uniq_by(array)
+
+
+def uniq_by(
+ array: t.Iterable[T], iteratee: t.Union[t.Callable[[T], t.Any], None] = None
+) -> t.List[T]:
+ """
+ This method is like :func:`uniq` except that it accepts iteratee which is invoked for each
+ element in array to generate the criterion by which uniqueness is computed. The order of result
+ values is determined by the order they occur in the array. The iteratee is invoked with one
+ argument: ``(value)``.
+
+ Args:
+ array: List to process.
+ iteratee: Function to transform the elements of the arrays. Defaults to
+ :func:`.identity`.
+
+ Returns:
+ Unique list.
+
+ Example:
+
+ >>> uniq_by([1, 2, 3, 1, 2, 3], lambda val: val % 2)
+ [1, 2]
+
+ .. versionadded:: 4.0.0
+ """
+ return list(iterunique(array, iteratee=iteratee))
+
+
+def uniq_with(
+ array: t.Sequence[T], comparator: t.Union[t.Callable[[T, T], t.Any], None] = None
+) -> t.List[T]:
+ """
+ This method is like :func:`uniq` except that it accepts comparator which is invoked to compare
+ elements of array. The order of result values is determined by the order they occur in the
+ array.The comparator is invoked with two arguments: ``(value, other)``.
+
+ Args:
+ array: List to process.
+ comparator: Function to compare the elements of the arrays. Defaults to
+ :func:`.is_equal`.
+
+ Returns:
+ Unique list.
+
+ Example:
+
+ >>> uniq_with([1, 2, 3, 4, 5], lambda a, b: (a % 2) == (b % 2))
+ [1, 2]
+
+ .. versionadded:: 4.0.0
+ """
+ return list(iterunique(array, comparator=comparator))
+
+
+def unshift(array: t.List[T], *items: T2) -> t.List[t.Union[T, T2]]:
+ """
+ Insert the given elements at the beginning of `array` and return the modified list.
+
+ Args:
+ array: List to modify.
+ items: Items to insert.
+
+ Returns:
+ Modified list.
+
+ Warning:
+ `array` is modified in place.
+
+ Example:
+
+ >>> array = [1, 2, 3, 4]
+ >>> unshift(array, -1, -2)
+ [-1, -2, 1, 2, 3, 4]
+ >>> array
+ [-1, -2, 1, 2, 3, 4]
+
+ .. versionadded:: 2.2.0
+ """
+ for item in reverse(items):
+ array.insert(0, item) # type: ignore
+
+ return array # type: ignore
+
+
+@t.overload
+def unzip(array: t.Iterable[t.Tuple[T, T2]]) -> t.List[t.Tuple[T, T2]]: ...
+
+
+@t.overload
+def unzip(array: t.Iterable[t.Tuple[T, T2, T3]]) -> t.List[t.Tuple[T, T2, T3]]: ...
+
+
+@t.overload
+def unzip(array: t.Iterable[t.Tuple[T, T2, T3, T4]]) -> t.List[t.Tuple[T, T2, T3, T4]]: ...
+
+
+@t.overload
+def unzip(array: t.Iterable[t.Tuple[T, T2, T3, T4, T5]]) -> t.List[t.Tuple[T, T2, T3, T4, T5]]: ...
+
+
+@t.overload
+def unzip(array: t.Iterable[t.Iterable[t.Any]]) -> t.List[t.Tuple[t.Any, ...]]: ...
+
+
+def unzip(array):
+ """
+ The inverse of :func:`zip_`, this method splits groups of elements into tuples composed of
+ elements from each group at their corresponding indexes.
+
+ Args:
+ array: List to process.
+
+ Returns:
+ Unzipped list.
+
+ Example:
+
+ >>> unzip([(1, 4, 7), (2, 5, 8), (3, 6, 9)])
+ [(1, 2, 3), (4, 5, 6), (7, 8, 9)]
+
+ .. versionadded:: 1.0.0
+
+ .. versionchanged:: 8.0.0
+ Support list of tuples instead.
+ """
+ return zip_(*array)
+
+
+@t.overload
+def unzip_with(
+ array: t.Iterable[t.Tuple[T, T2]],
+ iteratee: t.Union[
+ t.Callable[[t.Union[T, T2, T3], t.Union[T, T2], int], T3],
+ t.Callable[[t.Union[T, T2, T3], t.Union[T, T2]], T3],
+ t.Callable[[t.Union[T, T2, T3]], T3],
+ ],
+) -> t.List[T3]: ...
+
+
+@t.overload
+def unzip_with(
+ array: t.Iterable[t.Iterable[t.Any]],
+ iteratee: t.Union[
+ t.Callable[[t.Any, t.Any, int], T3],
+ t.Callable[[t.Any, t.Any], T3],
+ t.Callable[[t.Any], T3],
+ ],
+) -> t.List[T3]: ...
+
+
+@t.overload
+def unzip_with(
+ array: t.Iterable[t.Iterable[T]],
+ iteratee: None = None,
+) -> t.List[t.Tuple[T]]: ...
+
+
+def unzip_with(array, iteratee=None):
+ """
+ This method is like :func:`unzip` except that it accepts an iteratee to specify how regrouped
+ values should be combined. The iteratee is invoked with three arguments: ``(accumulator, value,
+ index)``.
+
+ Args:
+ array: List to process.
+ iteratee: Function to combine regrouped values.
+
+ Returns:
+ Unzipped list.
+
+ Example:
+
+ >>> from pydash import add
+ >>> unzip_with([(1, 10, 100), (2, 20, 200)], add)
+ [3, 30, 300]
+
+ .. versionadded:: 3.3.0
+ """
+ if not array:
+ return []
+
+ result = unzip(array)
+
+ if iteratee is None:
+ return result
+
+ def cbk(group):
+ return pyd.reduce_(group, iteratee)
+
+ return pyd.map_(result, cbk)
+
+
+def without(array: t.Iterable[T], *values: T) -> t.List[T]:
+ """
+ Creates an array with all occurrences of the passed values removed.
+
+ Args:
+ array: List to filter.
+ values: Values to remove.
+
+ Returns:
+ Filtered list.
+
+ Example:
+
+ >>> without([1, 2, 3, 2, 4, 4], 2, 4)
+ [1, 3]
+
+ .. versionadded:: 1.0.0
+ """
+ return [item for item in array if item not in values]
+
+
+def xor(array: t.Iterable[T], *lists: t.Iterable[T]) -> t.List[T]:
+ """
+ Creates a list that is the symmetric difference of the provided lists.
+
+ Args:
+ array: List to process.
+ *lists: Lists to xor with.
+
+ Returns:
+ XOR'd list.
+
+ Example:
+
+ >>> xor([1, 3, 4], [1, 2, 4], [2])
+ [3]
+
+ .. versionadded:: 1.0.0
+ """
+ return xor_by(array, *lists)
+
+
+@t.overload
+def xor_by(
+ array: t.Iterable[T],
+ *lists: t.Iterable[T],
+ iteratee: t.Union[t.Callable[[T], t.Any], IterateeObjT],
+) -> t.List[T]: ...
+
+
+@t.overload
+def xor_by(
+ array: t.Iterable[T], *lists: t.Union[t.Iterable[T], t.Callable[[T], t.Any]]
+) -> t.List[T]: ...
+
+
+def xor_by(array, *lists, **kwargs):
+ """
+ This method is like :func:`xor` except that it accepts iteratee which is invoked for each
+ element of each arras to generate the criterion by which they're compared. The order of result
+ values is determined by the order they occur in the arrays. The iteratee is invoked with one
+ argument: ``(value)``.
+
+ Args:
+ array: List to process.
+ *lists: Lists to xor with.
+
+ Keyword Args:
+ iteratee: Function to transform the elements of the arrays. Defaults to
+ :func:`.identity`.
+
+ Returns:
+ XOR'd list.
+
+ Example:
+
+ >>> xor_by([2.1, 1.2], [2.3, 3.4], round)
+ [1.2, 3.4]
+ >>> xor_by([{"x": 1}], [{"x": 2}, {"x": 1}], "x")
+ [{'x': 2}]
+
+ .. versionadded:: 4.0.0
+ """
+ if not lists:
+ return array[:]
+
+ iteratee, lists = parse_iteratee("iteratee", *lists, **kwargs)
+
+ return xor(
+ uniq(
+ difference_by(
+ array + lists[0],
+ intersection_by(array, lists[0], iteratee=iteratee),
+ iteratee=iteratee,
+ )
+ ),
+ *lists[1:],
+ )
+
+
+@t.overload
+def xor_with(
+ array: t.Sequence[T], *lists: t.Iterable[T2], comparator: t.Callable[[T, T2], t.Any]
+) -> t.List[T]: ...
+
+
+@t.overload
+def xor_with(
+ array: t.Sequence[T], *lists: t.Union[t.Iterable[T2], t.Callable[[T, T2], t.Any]]
+) -> t.List[T]: ...
+
+
+def xor_with(array, *lists, **kwargs):
+ """
+ This method is like :func:`xor` except that it accepts comparator which is invoked to compare
+ elements of arrays. The order of result values is determined by the order they occur in the
+ arrays. The comparator is invoked with two arguments: ``(arr_val, oth_val)``.
+
+ Args:
+ array: List to process.
+ *lists: Lists to xor with.
+
+ Keyword Args:
+ comparator: Function to compare the elements of the arrays. Defaults to
+ :func:`.is_equal`.
+
+ Returns:
+ XOR'd list.
+
+ Example:
+
+ >>> objects = [{"x": 1, "y": 2}, {"x": 2, "y": 1}]
+ >>> others = [{"x": 1, "y": 1}, {"x": 1, "y": 2}]
+ >>> expected = [{"y": 1, "x": 2}, {"y": 1, "x": 1}]
+ >>> xor_with(objects, others, lambda a, b: a == b) == expected
+ True
+
+ .. versionadded:: 4.0.0
+ """
+ if not lists:
+ return array[:]
+
+ comp, lists = parse_iteratee("comparator", *lists, **kwargs)
+
+ return xor_with(
+ uniq(
+ difference_with(
+ array + lists[0],
+ intersection_with(array, lists[0], comparator=comp),
+ comparator=comp,
+ )
+ ),
+ *lists[1:],
+ )
+
+
+@t.overload
+def zip_(array1: t.Iterable[T], array2: t.Iterable[T2], /) -> t.List[t.Tuple[T, T2]]: ...
+
+
+@t.overload
+def zip_(
+ array1: t.Iterable[T], array2: t.Iterable[T2], array3: t.Iterable[T3], /
+) -> t.List[t.Tuple[T, T2, T3]]: ...
+
+
+@t.overload
+def zip_(
+ array1: t.Iterable[T], array2: t.Iterable[T2], array3: t.Iterable[T3], array4: t.Iterable[T4], /
+) -> t.List[t.Tuple[T, T2, T3, T4]]: ...
+
+
+@t.overload
+def zip_(
+ array1: t.Iterable[T],
+ array2: t.Iterable[T2],
+ array3: t.Iterable[T3],
+ array4: t.Iterable[T4],
+ array5: t.Iterable[T5],
+ /,
+) -> t.List[t.Tuple[T, T2, T3, T4, T5]]: ...
+
+
+@t.overload
+def zip_(*arrays: t.Iterable[t.Any]) -> t.List[t.Tuple[t.Any, ...]]: ...
+
+
+def zip_(*arrays):
+ """
+ Groups the elements of each array at their corresponding indexes. Useful for separate data
+ sources that are coordinated through matching array indexes.
+
+ Args:
+ arrays: Lists to process.
+
+ Returns:
+ Zipped list.
+
+ Example:
+
+ >>> zip_([1, 2, 3], [4, 5, 6], [7, 8, 9])
+ [(1, 4, 7), (2, 5, 8), (3, 6, 9)]
+
+ .. versionadded:: 1.0.0
+
+ .. versionchanged:: 8.0.0
+ Return list of tuples instead of list of lists.
+ """
+ return list(zip(*arrays))
+
+
+@t.overload
+def zip_object(keys: t.Iterable[t.Tuple[T, T2]], values: None = None) -> t.Dict[T, T2]: ...
+
+
+@t.overload
+def zip_object(
+ keys: t.Iterable[t.List[t.Union[T, T2]]], values: None = None
+) -> t.Dict[t.Union[T, T2], t.Union[T, T2]]: ...
+
+
+@t.overload
+def zip_object(keys: t.Iterable[T], values: t.List[T2]) -> t.Dict[T, T2]: ...
+
+
+def zip_object(keys, values=None):
+ """
+ Creates a dict composed of lists of keys and values. Pass either a single two-dimensional list,
+ i.e. ``[[key1, value1], [key2, value2]]``, or two lists, one of keys and one of corresponding
+ values.
+
+ Args:
+ keys: Either a list of keys or a list of ``[key, value]`` pairs.
+ values: List of values to zip.
+
+ Returns:
+ Zipped dict.
+
+ Example:
+
+ >>> zip_object([1, 2, 3], [4, 5, 6])
+ {1: 4, 2: 5, 3: 6}
+
+ .. versionadded:: 1.0.0
+
+ .. versionchanged:: 4.0.0
+ Removed alias ``object_``.
+ """
+ if values is None:
+ keys_values = unzip(keys)
+ if len(keys_values) == 0:
+ keys, values = [], []
+ else:
+ keys, values = keys_values
+
+ return dict(zip(keys, values))
+
+
+def zip_object_deep(
+ keys: t.Iterable[t.Any], values: t.Union[t.List[t.Any], None] = None
+) -> t.Dict[t.Any, t.Any]:
+ """
+ This method is like :func:`zip_object` except that it supports property paths.
+
+ Args:
+ keys: Either a list of keys or a list of ``[key, value]`` pairs.
+ values: List of values to zip.
+
+ Returns:
+ Zipped dict.
+
+ Example:
+
+ >>> expected = {"a": {"b": {"c": 1, "d": 2}}}
+ >>> zip_object_deep(["a.b.c", "a.b.d"], [1, 2]) == expected
+ True
+
+ .. versionadded:: 4.0.0
+ """
+ if values is None:
+ keys_values = unzip(keys)
+ if len(keys_values) == 0:
+ keys, values = [], []
+ else:
+ keys, values = keys_values
+
+ obj: t.Dict[t.Any, t.Any] = {}
+ for idx, key in enumerate(keys):
+ obj = pyd.set_(obj, key, pyd.get(values, idx))
+
+ return obj
+
+
+@t.overload
+def zip_with(
+ array1: t.Iterable[T],
+ array2: t.Iterable[T2],
+ *,
+ iteratee: t.Union[
+ t.Callable[[T, T2, int], T3],
+ t.Callable[[T, T2], T3],
+ t.Callable[[T], T3],
+ ],
+) -> t.List[T3]: ...
+
+
+@t.overload
+def zip_with(
+ *arrays: t.Iterable[t.Any],
+ iteratee: t.Union[
+ t.Callable[[t.Any, t.Any, int], T2],
+ t.Callable[[t.Any, t.Any], T2],
+ t.Callable[[t.Any], T2],
+ ],
+) -> t.List[T2]: ...
+
+
+@t.overload
+def zip_with(
+ *arrays: t.Union[
+ t.Iterable[t.Any],
+ t.Callable[[t.Any, t.Any, int], T2],
+ t.Callable[[t.Any, t.Any], T2],
+ t.Callable[[t.Any], T2],
+ ],
+) -> t.List[T2]: ...
+
+
+def zip_with(*arrays, **kwargs):
+ """
+ This method is like :func:`zip` except that it accepts an iteratee to specify how grouped values
+ should be combined. The iteratee is invoked with three arguments:
+ ``(accumulator, value, index)``.
+
+ Args:
+ *arrays: Lists to process.
+
+ Keyword Args:
+ iteratee (callable): Function to combine grouped values.
+
+ Returns:
+ Zipped list of grouped elements.
+
+ Example:
+
+ >>> from pydash import add
+ >>> zip_with([1, 2], [10, 20], [100, 200], add)
+ [111, 222]
+ >>> zip_with([1, 2], [10, 20], [100, 200], iteratee=add)
+ [111, 222]
+
+ .. versionadded:: 3.3.0
+ """
+ if "iteratee" in kwargs:
+ iteratee = kwargs["iteratee"]
+ elif len(arrays) > 1:
+ iteratee = arrays[-1]
+ arrays = arrays[:-1]
+ else:
+ iteratee = None
+
+ return unzip_with(arrays, iteratee)
+
+
+#
+# Utility methods not a part of the main API
+#
+
+
+def iterflatten(array, depth=-1):
+ """Iteratively flatten a list shallowly or deeply."""
+ for item in array:
+ if isinstance(item, (list, tuple)) and depth != 0:
+ for subitem in iterflatten(item, depth - 1):
+ yield subitem
+ else:
+ yield item
+
+
+def iterinterleave(*arrays):
+ """Interleave multiple lists."""
+ iters = [iter(arr) for arr in arrays]
+
+ while iters:
+ nextiters = []
+ for itr in iters:
+ try:
+ yield next(itr)
+ nextiters.append(itr)
+ except StopIteration:
+ pass
+
+ iters = nextiters
+
+
+def iterintersperse(iterable, separator):
+ """Iteratively intersperse iterable."""
+ iterable = iter(iterable)
+ yield next(iterable)
+ for item in iterable:
+ yield separator
+ yield item
+
+
+def iterunique(array, comparator=None, iteratee=None): # noqa: PLR0912
+ """Yield each unique item in array."""
+ if not array: # pragma: no cover
+ return
+
+ if iteratee is not None:
+ iteratee = pyd.iteratee(iteratee)
+
+ seen_hashable = set()
+ seen_unhashable = []
+
+ for item in array:
+ if iteratee is None:
+ cmp_item = item
+ else:
+ cmp_item = iteratee(item)
+
+ if comparator is None:
+ try:
+ if cmp_item not in seen_hashable:
+ yield item
+ seen_hashable.add(cmp_item)
+ except TypeError:
+ if cmp_item not in seen_unhashable:
+ yield item
+ seen_unhashable.append(cmp_item)
+ else:
+ unseen = True
+ for seen_item in seen_unhashable:
+ if comparator(cmp_item, seen_item):
+ unseen = False
+ break
+ if unseen:
+ yield item
+ seen_unhashable.append(cmp_item)
+
+
+def iterduplicates(array):
+ """Yield duplictes found in `array`."""
+ seen = []
+ for i, item in enumerate(array):
+ if item in seen:
+ yield i, item
+ else:
+ seen.append(item)
+
+
+def iterintersection(array, other, comparator=None, iteratee=None):
+ """Yield intersecting values between `array` and `other` using `comparator` to determine if they
+ intersect."""
+ if not array or not other: # pragma: no cover
+ return
+
+ if comparator is None:
+ comparator = pyd.is_equal
+
+ iteratee = pyd.iteratee(iteratee)
+
+ # NOTE: Maintain ordering of yielded values based on `array` ordering.
+ seen = []
+ for item in array:
+ cmp_item = iteratee(item)
+
+ if cmp_item in seen:
+ continue
+
+ seen.append(cmp_item)
+ seen_others = []
+
+ for value in other:
+ cmp_value = iteratee(value)
+
+ if cmp_value in seen_others:
+ continue
+
+ seen_others.append(cmp_value)
+
+ if comparator(cmp_item, cmp_value):
+ yield item
+ break
+
+
+def iterdifference(array, other, comparator=None, iteratee=None):
+ """Yield different values in `array` as compared to `other` using `comparator` to determine if
+ they are different."""
+ if not array or not other: # pragma: no cover
+ return
+
+ if comparator is None:
+ comparator = pyd.is_equal
+
+ iteratee = pyd.iteratee(iteratee)
+
+ def is_different(item, seen):
+ is_diff = True
+
+ if item not in seen:
+ for value in other:
+ if comparator(iteratee(item), iteratee(value)):
+ is_diff = False
+ break
+
+ if is_diff:
+ seen.append(item)
+ return is_diff
+
+ seen = []
+ not_seen = []
+
+ for item in array:
+ if item in not_seen or is_different(item, seen):
+ yield item