Skip to content

Seq

Bases: CommonMethods[T], Sequence[T]

Seq represent an in memory Sequence.

Implements the Sequence Protocol from collections.abc, so it can be used as a standard immutable sequence.

Provides a subset of Iter methods with eager evaluation, and is the return type of Iter.collect().

The underlying data structure is an immutable tuple, hence the memory efficiency is better than a Vec.

You can create a Seq from any Iterable (like a list, or polars.Series) or unpacked values using the from_ class method.

If you already have a tuple, simply pass it to the constructor, without runtime checks.

Parameters:

Name Type Description Default
data tuple[T, ...]

The data to initialize the Seq with.

required
Source code in src/pyochain/_iter/_main.py
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
class Seq[T](CommonMethods[T], Sequence[T]):
    """`Seq` represent an in memory Sequence.

    Implements the `Sequence` Protocol from `collections.abc`, so it can be used as a standard immutable sequence.

    Provides a subset of `Iter` methods with eager evaluation, and is the return type of `Iter.collect()`.

    The underlying data structure is an immutable tuple, hence the memory efficiency is better than a `Vec`.

    You can create a `Seq` from any `Iterable` (like a list, or polars.Series) or unpacked values using the `from_` class method.

    If you already have a tuple, simply pass it to the constructor, without runtime checks.

    Args:
            data (tuple[T, ...]): The data to initialize the Seq with.
    """

    _inner: tuple[T, ...]

    __slots__ = ("_inner",)

    def __init__(self, data: tuple[T, ...]) -> None:
        self._inner = data  # pyright: ignore[reportIncompatibleVariableOverride]

    @overload
    def __getitem__(self, index: int) -> T: ...
    @overload
    def __getitem__(self, index: slice) -> Sequence[T]: ...
    def __getitem__(self, index: int | slice[Any, Any, Any]) -> T | Sequence[T]:
        return self._inner.__getitem__(index)

    def __len__(self) -> int:
        return len(self._inner)

    @overload
    @staticmethod
    def from_[U](data: Iterable[U]) -> Seq[U]: ...
    @overload
    @staticmethod
    def from_[U](data: U, *more_data: U) -> Seq[U]: ...
    @staticmethod
    def from_[U](data: Iterable[U] | U, *more_data: U) -> Seq[U]:
        """Create a `Seq` from an `Iterable` or unpacked values.

        Prefer using the standard constructor, as this method involves extra checks and conversions steps.

        Args:
            data (Iterable[U] | U): Iterable to convert into a sequence, or a single value.
            *more_data (U): Unpacked items to include in the sequence, if 'data' is not an Iterable.

        Returns:
            Seq[U]: A new Seq instance containing the provided data.

        Examples:
        ```python
        >>> import pyochain as pc
        >>> pc.Seq.from_(1, 2, 3)
        Seq(1, 2, 3)

        ```
        """
        converted = _convert_data(data, *more_data)
        return Seq(converted if isinstance(converted, tuple) else tuple(converted))  # ty:ignore[invalid-argument-type] # ty doesn't seem to correctly narrow here

    def iter(self) -> Iter[T]:
        """Get an iterator over the sequence.

        Call this to switch to lazy evaluation.

        Returns:
            Iter[T]: An `Iter` instance wrapping an iterator over the sequence.
        """
        return self._lazy(iter)

    def for_each[**P](
        self,
        func: Callable[Concatenate[T, P], Any],
        *args: P.args,
        **kwargs: P.kwargs,
    ) -> Self:
        """Iterate over the elements and apply a function to each.

        Contratry to `Iter.for_each`, this method returns the same instance for chaining.

        Args:
            func (Callable[Concatenate[T, P], Any]): Function to apply to each element.
            *args (P.args): Positional arguments for the function.
            **kwargs (P.kwargs): Keyword arguments for the function.

        Returns:
            Self: The same instance for chaining.

        Examples:
        ```python
        ```
        """
        for v in self._inner:
            func(v, *args, **kwargs)
        return self

    def is_distinct(self) -> bool:
        """Return True if all items are distinct.

        Returns:
            bool: True if all items are distinct, False otherwise.

        ```python
        >>> import pyochain as pc
        >>> pc.Seq([1, 2]).is_distinct()
        True

        ```
        """
        return self.into(cz.itertoolz.isdistinct)

all

all(predicate: Callable[[T], bool] = lambda x: bool(x)) -> bool

Tests if every element of the iterator matches a predicate.

Iter.all() takes a closure that returns true or false.

It applies this closure to each element of the iterator, and if they all return true, then so does Iter.all().

If any of them return false, it returns false.

An empty iterator returns true.

Parameters:

Name Type Description Default
predicate Callable[[T], bool]

Function to evaluate each item. Defaults to checking truthiness.

lambda x: bool(x)

Returns:

Name Type Description
bool bool

True if all elements match the predicate, False otherwise.

Example:

>>> import pyochain as pc
>>> pc.Seq([1, True]).all()
True
>>> pc.Seq([]).all()
True
>>> pc.Seq([1, 0]).all()
False
>>> def is_even(x: int) -> bool:
...     return x % 2 == 0
>>> pc.Seq([2, 4, 6]).all(is_even)
True

Source code in src/pyochain/_iter/_booleans.py
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
def all(self, predicate: Callable[[T], bool] = lambda x: bool(x)) -> bool:
    """Tests if every element of the iterator matches a predicate.

    `Iter.all()` takes a closure that returns true or false.

    It applies this closure to each element of the iterator, and if they all return true, then so does `Iter.all()`.

    If any of them return false, it returns false.

    An empty iterator returns true.

    Args:
        predicate (Callable[[T], bool]): Function to evaluate each item. Defaults to checking truthiness.

    Returns:
        bool: True if all elements match the predicate, False otherwise.

    Example:
    ```python
    >>> import pyochain as pc
    >>> pc.Seq([1, True]).all()
    True
    >>> pc.Seq([]).all()
    True
    >>> pc.Seq([1, 0]).all()
    False
    >>> def is_even(x: int) -> bool:
    ...     return x % 2 == 0
    >>> pc.Seq([2, 4, 6]).all(is_even)
    True

    ```
    """

    def _all(data: Iterable[T]) -> bool:
        return all(predicate(x) for x in data)

    return self.into(_all)

all_equal

all_equal(key: Callable[[T], U] | None = None) -> bool

Return True if all items are equal.

Parameters:

Name Type Description Default
key Callable[[T], U] | None

Function to transform items before comparison. Defaults to None.

None

Returns:

Name Type Description
bool bool

True if all items are equal, False otherwise.

Example:

>>> import pyochain as pc
>>> pc.Seq([1, 1, 1]).all_equal()
True
A function that accepts a single argument and returns a transformed version of each input item can be specified with key:
>>> pc.Seq("AaaA").all_equal(key=str.casefold)
True
>>> pc.Seq([1, 2, 3]).all_equal(key=lambda x: x < 10)
True

Source code in src/pyochain/_iter/_booleans.py
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
def all_equal[U](self, key: Callable[[T], U] | None = None) -> bool:
    """Return True if all items are equal.

    Args:
        key (Callable[[T], U] | None): Function to transform items before comparison. Defaults to None.

    Returns:
        bool: True if all items are equal, False otherwise.

    Example:
    ```python
    >>> import pyochain as pc
    >>> pc.Seq([1, 1, 1]).all_equal()
    True

    ```
    A function that accepts a single argument and returns a transformed version of each input item can be specified with key:
    ```python
    >>> pc.Seq("AaaA").all_equal(key=str.casefold)
    True
    >>> pc.Seq([1, 2, 3]).all_equal(key=lambda x: x < 10)
    True

    ```
    """
    return self.into(mit.all_equal, key=key)

all_unique

all_unique(key: Callable[[T], U] | None = None) -> bool

Returns True if all the elements of iterable are unique.

Parameters:

Name Type Description Default
key Callable[[T], U] | None

Function to transform items before comparison. Defaults to None.

None

Returns:

Name Type Description
bool bool

True if all elements are unique, False otherwise.

Example:

>>> import pyochain as pc
>>> pc.Seq("ABCB").all_unique()
False
If a key function is specified, it will be used to make comparisons.
>>> pc.Seq("ABCb").all_unique()
True
>>> pc.Seq("ABCb").all_unique(str.lower)
False
The function returns as soon as the first non-unique element is encountered.

Iterables with a mix of hashable and unhashable items can be used, but the function will be slower for unhashable items

Source code in src/pyochain/_iter/_booleans.py
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
def all_unique[U](self, key: Callable[[T], U] | None = None) -> bool:
    """Returns True if all the elements of iterable are unique.

    Args:
        key (Callable[[T], U] | None): Function to transform items before comparison. Defaults to None.

    Returns:
        bool: True if all elements are unique, False otherwise.

    Example:
    ```python
    >>> import pyochain as pc
    >>> pc.Seq("ABCB").all_unique()
    False

    ```
    If a key function is specified, it will be used to make comparisons.
    ```python
    >>> pc.Seq("ABCb").all_unique()
    True
    >>> pc.Seq("ABCb").all_unique(str.lower)
    False

    ```
    The function returns as soon as the first non-unique element is encountered.

    Iterables with a mix of hashable and unhashable items can be used, but the function will be slower for unhashable items

    """
    return self.into(mit.all_unique, key=key)

any

any(predicate: Callable[[T], bool] = lambda x: bool(x)) -> bool

Tests if any element of the iterator matches a predicate.

Iter.any() takes a closure that returns true or false.

It applies this closure to each element of the iterator, and if any of them return true, then so does Iter.any().

If they all return false, it returns false.

An empty iterator returns false.

Parameters:

Name Type Description Default
predicate Callable[[T], bool]

Function to evaluate each item. Defaults to checking truthiness.

lambda x: bool(x)

Returns:

Name Type Description
bool bool

True if any element matches the predicate, False otherwise.

Example:

>>> import pyochain as pc
>>> pc.Seq([0, 1]).any()
True
>>> pc.Seq(range(0)).any()
False
>>> def is_even(x: int) -> bool:
...     return x % 2 == 0
>>> pc.Seq([1, 3, 4]).any(is_even)
True

Source code in src/pyochain/_iter/_booleans.py
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
def any(self, predicate: Callable[[T], bool] = lambda x: bool(x)) -> bool:
    """Tests if any element of the iterator matches a predicate.

    `Iter.any()` takes a closure that returns true or false.

    It applies this closure to each element of the iterator, and if any of them return true, then so does `Iter.any()`.

    If they all return false, it returns false.

    An empty iterator returns false.

    Args:
        predicate (Callable[[T], bool]): Function to evaluate each item. Defaults to checking truthiness.

    Returns:
        bool: True if any element matches the predicate, False otherwise.

    Example:
    ```python
    >>> import pyochain as pc
    >>> pc.Seq([0, 1]).any()
    True
    >>> pc.Seq(range(0)).any()
    False
    >>> def is_even(x: int) -> bool:
    ...     return x % 2 == 0
    >>> pc.Seq([1, 3, 4]).any(is_even)
    True

    ```
    """

    def _any(data: Iterable[T]) -> bool:
        return any(predicate(x) for x in data)

    return self.into(_any)

argmax

argmax(key: Callable[[T], U] | None = None) -> int

Index of the first occurrence of a maximum value in an iterable.

Parameters:

Name Type Description Default
key Callable[[T], U] | None

Optional function to determine the value for comparison.

None

Returns:

Name Type Description
int int

The index of the maximum value.

>>> import pyochain as pc
>>> pc.Seq("abcdefghabcd").argmax()
7
>>> pc.Seq([0, 1, 2, 3, 3, 2, 1, 0]).argmax()
3
For example, identify the best machine learning model:
>>> models = pc.Seq(["svm", "random forest", "knn", "naïve bayes"])
>>> accuracy = pc.Seq([68, 61, 84, 72])
>>> # Most accurate model
>>> models.nth(accuracy.argmax())
'knn'
>>>
>>> # Best accuracy
>>> accuracy.into(max)
84

Source code in src/pyochain/_iter/_aggregations.py
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
def argmax[U](self, key: Callable[[T], U] | None = None) -> int:
    """Index of the first occurrence of a maximum value in an iterable.

    Args:
        key (Callable[[T], U] | None): Optional function to determine the value for comparison.

    Returns:
        int: The index of the maximum value.

    ```python
    >>> import pyochain as pc
    >>> pc.Seq("abcdefghabcd").argmax()
    7
    >>> pc.Seq([0, 1, 2, 3, 3, 2, 1, 0]).argmax()
    3

    ```
    For example, identify the best machine learning model:
    ```python
    >>> models = pc.Seq(["svm", "random forest", "knn", "naïve bayes"])
    >>> accuracy = pc.Seq([68, 61, 84, 72])
    >>> # Most accurate model
    >>> models.nth(accuracy.argmax())
    'knn'
    >>>
    >>> # Best accuracy
    >>> accuracy.into(max)
    84

    ```
    """
    return self.into(mit.argmax, key=key)

argmin

argmin(key: Callable[[T], U] | None = None) -> int

Index of the first occurrence of a minimum value in an iterable.

Parameters:

Name Type Description Default
key Callable[[T], U] | None

Optional function to determine the value for comparison.

None

Returns:

Name Type Description
int int

The index of the minimum value.

>>> import pyochain as pc
>>> pc.Seq("efghabcdijkl").argmin()
4
>>> pc.Seq([3, 2, 1, 0, 4, 2, 1, 0]).argmin()
3

For example, look up a label corresponding to the position of a value that minimizes a cost function:

>>> def cost(x):
...     "Days for a wound to heal given a subject's age."
...     return x**2 - 20 * x + 150
>>> labels = pc.Seq(["homer", "marge", "bart", "lisa", "maggie"])
>>> ages = pc.Seq([35, 30, 10, 9, 1])
>>> # Fastest healing family member
>>> labels.nth(ages.argmin(key=cost))
'bart'
>>> # Age with fastest healing
>>> ages.into(min, key=cost)
10

Source code in src/pyochain/_iter/_aggregations.py
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
def argmin[U](self, key: Callable[[T], U] | None = None) -> int:
    """Index of the first occurrence of a minimum value in an iterable.

    Args:
        key (Callable[[T], U] | None): Optional function to determine the value for comparison.

    Returns:
        int: The index of the minimum value.

    ```python
    >>> import pyochain as pc
    >>> pc.Seq("efghabcdijkl").argmin()
    4
    >>> pc.Seq([3, 2, 1, 0, 4, 2, 1, 0]).argmin()
    3

    ```

    For example, look up a label corresponding to the position of a value that minimizes a cost function:
    ```python
    >>> def cost(x):
    ...     "Days for a wound to heal given a subject's age."
    ...     return x**2 - 20 * x + 150
    >>> labels = pc.Seq(["homer", "marge", "bart", "lisa", "maggie"])
    >>> ages = pc.Seq([35, 30, 10, 9, 1])
    >>> # Fastest healing family member
    >>> labels.nth(ages.argmin(key=cost))
    'bart'
    >>> # Age with fastest healing
    >>> ages.into(min, key=cost)
    10

    ```
    """
    return self.into(mit.argmin, key=key)

combination_index

combination_index(r: Iterable[T]) -> int

Computes the index of the first element, without computing the previous combinations.

The subsequences of iterable that are of length r can be ordered lexicographically.

ValueError will be raised if the given element isn't one of the combinations of iterable.

Equivalent to list(combinations(iterable, r)).index(element).

Parameters:

Name Type Description Default
r Iterable[T]

The combination to find the index of.

required

Returns:

Name Type Description
int int

The index of the combination.

>>> import pyochain as pc
>>> pc.Seq("abcdefg").combination_index("adf")
10
Source code in src/pyochain/_iter/_aggregations.py
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
def combination_index(self, r: Iterable[T]) -> int:
    """Computes the index of the first element, without computing the previous combinations.

    The subsequences of iterable that are of length r can be ordered lexicographically.


    ValueError will be raised if the given element isn't one of the combinations of iterable.

    Equivalent to list(combinations(iterable, r)).index(element).

    Args:
        r (Iterable[T]): The combination to find the index of.

    Returns:
        int: The index of the combination.

    ```python
    >>> import pyochain as pc
    >>> pc.Seq("abcdefg").combination_index("adf")
    10

    ```
    """
    return self.into(functools.partial(mit.combination_index, r))

count_by

count_by(key: Callable[[T], K]) -> Dict[K, int]

Count elements of a collection by a key function.

Parameters:

Name Type Description Default
key Callable[[T], K]

Function to compute the key for counting.

required

Returns:

Type Description
Dict[K, int]

Dict[K, int]: Dict with count of elements for each key.

Example:

>>> import pyochain as pc
>>> pc.Iter(["cat", "mouse", "dog"]).count_by(len)
{3: 2, 5: 1}
>>> def iseven(x):
...     return x % 2 == 0
>>> pc.Iter([1, 2, 3]).count_by(iseven)
{False: 2, True: 1}

Source code in src/pyochain/_iter/_dicts.py
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
def count_by[K](self, key: Callable[[T], K]) -> Dict[K, int]:
    """Count elements of a collection by a key function.

    Args:
        key (Callable[[T], K]): Function to compute the key for counting.

    Returns:
        Dict[K, int]: Dict with count of elements for each key.

    Example:
    ```python
    >>> import pyochain as pc
    >>> pc.Iter(["cat", "mouse", "dog"]).count_by(len)
    {3: 2, 5: 1}
    >>> def iseven(x):
    ...     return x % 2 == 0
    >>> pc.Iter([1, 2, 3]).count_by(iseven)
    {False: 2, True: 1}

    ```
    """
    from .._dict import Dict

    def _count_by(data: Iterable[T]) -> Dict[K, int]:
        return Dict(cz.recipes.countby(key, data))

    return self.into(_count_by)

diff_symmetric

diff_symmetric(*others: Iterable[T]) -> Seq[T]

Return the symmetric difference (XOR) of this iterable and 'others'.

(Elements in either 'self' or 'others' but not in both).

See Also: - intersection - difference

Note

This method consumes inner data, unsorts it, and removes duplicates.

Parameters:

Name Type Description Default
*others Iterable[T]

Other iterables to compute the symmetric difference with.

()

Returns:

Type Description
Seq[T]

Seq[T]: A new Seq containing the symmetric difference of elements.

Example:

>>> import pyochain as pc
>>> pc.Seq([1, 2, 2]).diff_symmetric([2, 3]).iter().sort()
Vec(1, 3)
>>> pc.Seq([1, 2, 3]).diff_symmetric([3, 4, 5]).iter().sort()
Vec(1, 2, 4, 5)

Source code in src/pyochain/_iter/_eager.py
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
def diff_symmetric(self, *others: Iterable[T]) -> Seq[T]:
    """Return the symmetric difference (XOR) of this iterable and 'others'.

    (Elements in either 'self' or 'others' but not in both).

    **See Also**:
        - `intersection`
        - `difference`

    Note:
        This method consumes inner data, unsorts it, and removes duplicates.

    Args:
        *others (Iterable[T]): Other iterables to compute the symmetric difference with.

    Returns:
        Seq[T]: A new Seq containing the symmetric difference of elements.

    Example:
    ```python
    >>> import pyochain as pc
    >>> pc.Seq([1, 2, 2]).diff_symmetric([2, 3]).iter().sort()
    Vec(1, 3)
    >>> pc.Seq([1, 2, 3]).diff_symmetric([3, 4, 5]).iter().sort()
    Vec(1, 2, 4, 5)

    ```
    """

    def _symmetric_difference(data: Iterable[T]) -> tuple[T, ...]:
        return tuple(set(data).symmetric_difference(*others))

    return self._eager(_symmetric_difference)

difference

difference(*others: Iterable[T]) -> Seq[T]

Return the difference of this iterable and 'others'.

See Also
  • intersection
  • diff_symmetric
Note

This method consumes inner data, unsorts it, and removes duplicates.

Parameters:

Name Type Description Default
*others Iterable[T]

Other iterables to subtract from this iterable.

()

Returns:

Type Description
Seq[T]

Seq[T]: A new Seq containing the difference of elements.

Example:

>>> import pyochain as pc
>>> pc.Seq([1, 2, 2]).difference([2, 3])
Seq(1,)

Source code in src/pyochain/_iter/_eager.py
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
def difference(self, *others: Iterable[T]) -> Seq[T]:
    """Return the difference of this iterable and 'others'.

    See Also:
        - `intersection`
        - `diff_symmetric`

    Note:
        This method consumes inner data, unsorts it, and removes duplicates.

    Args:
        *others (Iterable[T]): Other iterables to subtract from this iterable.

    Returns:
        Seq[T]: A new Seq containing the difference of elements.

    Example:
    ```python
    >>> import pyochain as pc
    >>> pc.Seq([1, 2, 2]).difference([2, 3])
    Seq(1,)

    ```
    """

    def _difference(data: Iterable[T]) -> tuple[T, ...]:
        return tuple(set(data).difference(*others))

    return self._eager(_difference)

eq

eq(other: Self | T) -> bool

Check if two records are equal based on their data.

Parameters:

Name Type Description Default
other Self | T

Another instance or corresponding underlying data to compare against.

required

Returns:

Name Type Description
bool bool

True if the underlying data are equal, False otherwise.

Example:

>>> import pyochain as pc
>>> d1 = pc.Dict({"a": 1, "b": 2})
>>> d2 = pc.Dict({"a": 1, "b": 2})
>>> d3 = pc.Dict({"a": 1, "b": 3})
>>> d1.eq(d2)
True
>>> d1.eq(d3)
False

Source code in src/pyochain/_core/_main.py
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
def eq(self, other: Self | T) -> bool:
    """Check if two records are equal based on their data.

    Args:
        other (Self | T): Another instance or corresponding underlying data to compare against.

    Returns:
        bool: True if the underlying data are equal, False otherwise.

    Example:
    ```python
    >>> import pyochain as pc
    >>> d1 = pc.Dict({"a": 1, "b": 2})
    >>> d2 = pc.Dict({"a": 1, "b": 2})
    >>> d3 = pc.Dict({"a": 1, "b": 3})
    >>> d1.eq(d2)
    True
    >>> d1.eq(d3)
    False

    ```
    """
    other_data = other._inner if isinstance(other, self.__class__) else other
    return self._inner == other_data

find

find(predicate: Callable[[T], bool]) -> Option[T]

Searches for an element of an iterator that satisfies a predicate.

Takes a closure that returns true or false as predicate, and applies it to each element of the iterator.

Parameters:

Name Type Description Default
predicate Callable[[T], bool]

Function to evaluate each item.

required

Returns:

Type Description
Option[T]

Option[T]: The first element satisfying the predicate. Some(value) if found, NONE otherwise.

Example:

>>> import pyochain as pc
>>> def gt_five(x: int) -> bool:
...     return x > 5
>>>
>>> def gt_nine(x: int) -> bool:
...     return x > 9
>>>
>>> pc.Seq(range(10)).find(predicate=gt_five)
Some(6)
>>> pc.Seq(range(10)).find(predicate=gt_nine).unwrap_or("missing")
'missing'

Source code in src/pyochain/_iter/_booleans.py
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
def find(
    self,
    predicate: Callable[[T], bool],
) -> Option[T]:
    """Searches for an element of an iterator that satisfies a `predicate`.

    Takes a closure that returns true or false as `predicate`, and applies it to each element of the iterator.

    Args:
        predicate (Callable[[T], bool]): Function to evaluate each item.

    Returns:
        Option[T]: The first element satisfying the predicate. `Some(value)` if found, `NONE` otherwise.

    Example:
    ```python
    >>> import pyochain as pc
    >>> def gt_five(x: int) -> bool:
    ...     return x > 5
    >>>
    >>> def gt_nine(x: int) -> bool:
    ...     return x > 9
    >>>
    >>> pc.Seq(range(10)).find(predicate=gt_five)
    Some(6)
    >>> pc.Seq(range(10)).find(predicate=gt_nine).unwrap_or("missing")
    'missing'

    ```
    """
    from .._results import Option

    def _find(data: Iterable[T]) -> Option[T]:
        return Option.from_(next(filter(predicate, data), None))

    return self.into(_find)

first

first() -> T

Return the first element.

Returns:

Name Type Description
T T

The first element of the iterable.

>>> import pyochain as pc
>>> pc.Seq([9]).first()
9
Source code in src/pyochain/_iter/_aggregations.py
128
129
130
131
132
133
134
135
136
137
138
139
140
141
def first(self) -> T:
    """Return the first element.

    Returns:
        T: The first element of the iterable.

    ```python
    >>> import pyochain as pc
    >>> pc.Seq([9]).first()
    9

    ```
    """
    return self.into(cz.itertoolz.first)

for_each

for_each(
    func: Callable[Concatenate[T, P], Any], *args: P.args, **kwargs: P.kwargs
) -> Self

Iterate over the elements and apply a function to each.

Contratry to Iter.for_each, this method returns the same instance for chaining.

Parameters:

Name Type Description Default
func Callable[Concatenate[T, P], Any]

Function to apply to each element.

required
*args P.args

Positional arguments for the function.

()
**kwargs P.kwargs

Keyword arguments for the function.

{}

Returns:

Name Type Description
Self Self

The same instance for chaining.

Examples:


Source code in src/pyochain/_iter/_main.py
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
def for_each[**P](
    self,
    func: Callable[Concatenate[T, P], Any],
    *args: P.args,
    **kwargs: P.kwargs,
) -> Self:
    """Iterate over the elements and apply a function to each.

    Contratry to `Iter.for_each`, this method returns the same instance for chaining.

    Args:
        func (Callable[Concatenate[T, P], Any]): Function to apply to each element.
        *args (P.args): Positional arguments for the function.
        **kwargs (P.kwargs): Keyword arguments for the function.

    Returns:
        Self: The same instance for chaining.

    Examples:
    ```python
    ```
    """
    for v in self._inner:
        func(v, *args, **kwargs)
    return self

frequencies

frequencies() -> Dict[T, int]

Find number of occurrences of each value in the iterable.

Returns:

Type Description
Dict[T, int]

Dict[T, int]: Dict with element frequencies as counts.

>>> import pyochain as pc
>>> data = ["cat", "cat", "ox", "pig", "pig", "cat"]
>>> pc.Iter(data).frequencies()
{'cat': 3, 'ox': 1, 'pig': 2}
Source code in src/pyochain/_iter/_dicts.py
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
def frequencies(self) -> Dict[T, int]:
    """Find number of occurrences of each value in the iterable.

    Returns:
        Dict[T, int]: Dict with element frequencies as counts.

    ```python
    >>> import pyochain as pc
    >>> data = ["cat", "cat", "ox", "pig", "pig", "cat"]
    >>> pc.Iter(data).frequencies()
    {'cat': 3, 'ox': 1, 'pig': 2}

    ```
    """
    from .._dict import Dict

    def _frequencies(data: Iterable[T]) -> Dict[T, int]:
        return Dict(cz.itertoolz.frequencies(data))

    return self.into(_frequencies)

from_ staticmethod

from_(data: Iterable[U]) -> Seq[U]
from_(data: U, *more_data: U) -> Seq[U]
from_(data: Iterable[U] | U, *more_data: U) -> Seq[U]

Create a Seq from an Iterable or unpacked values.

Prefer using the standard constructor, as this method involves extra checks and conversions steps.

Parameters:

Name Type Description Default
data Iterable[U] | U

Iterable to convert into a sequence, or a single value.

required
*more_data U

Unpacked items to include in the sequence, if 'data' is not an Iterable.

()

Returns:

Type Description
Seq[U]

Seq[U]: A new Seq instance containing the provided data.

Examples:

>>> import pyochain as pc
>>> pc.Seq.from_(1, 2, 3)
Seq(1, 2, 3)

Source code in src/pyochain/_iter/_main.py
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
@staticmethod
def from_[U](data: Iterable[U] | U, *more_data: U) -> Seq[U]:
    """Create a `Seq` from an `Iterable` or unpacked values.

    Prefer using the standard constructor, as this method involves extra checks and conversions steps.

    Args:
        data (Iterable[U] | U): Iterable to convert into a sequence, or a single value.
        *more_data (U): Unpacked items to include in the sequence, if 'data' is not an Iterable.

    Returns:
        Seq[U]: A new Seq instance containing the provided data.

    Examples:
    ```python
    >>> import pyochain as pc
    >>> pc.Seq.from_(1, 2, 3)
    Seq(1, 2, 3)

    ```
    """
    converted = _convert_data(data, *more_data)
    return Seq(converted if isinstance(converted, tuple) else tuple(converted))  # ty:ignore[invalid-argument-type] # ty doesn't seem to correctly narrow here

group_by

group_by(on: Callable[[T], K]) -> Dict[K, list[T]]

Group elements by key function and return a Dict result.

Parameters:

Name Type Description Default
on Callable[[T], K]

Function to compute the key for grouping.

required

Returns:

Type Description
Dict[K, list[T]]

Dict[K, list[T]]: Dict with grouped elements as lists.

Example:

>>> import pyochain as pc
>>> names = [
...     "Alice",
...     "Bob",
...     "Charlie",
...     "Dan",
...     "Edith",
...     "Frank",
... ]
>>> pc.Iter(names).group_by(len).sort()
... # doctest: +NORMALIZE_WHITESPACE
{3: ['Bob', 'Dan'], 5: ['Alice', 'Edith', 'Frank'], 7: ['Charlie']}
>>>
>>> iseven = lambda x: x % 2 == 0
>>> pc.Iter([1, 2, 3, 4, 5, 6, 7, 8]).group_by(iseven)
... # doctest: +NORMALIZE_WHITESPACE
{False: [1, 3, 5, 7], True: [2, 4, 6, 8]}
Non-callable keys imply grouping on a member.
>>> data = [
...     {"name": "Alice", "gender": "F"},
...     {"name": "Bob", "gender": "M"},
...     {"name": "Charlie", "gender": "M"},
... ]
>>> pc.Iter(data).group_by("gender").sort()
... # doctest: +NORMALIZE_WHITESPACE
{'F': [{'gender': 'F', 'name': 'Alice'}],
'M': [{'gender': 'M', 'name': 'Bob'}, {'gender': 'M', 'name': 'Charlie'}]}

Source code in src/pyochain/_iter/_dicts.py
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
def group_by[K](self, on: Callable[[T], K]) -> Dict[K, list[T]]:
    """Group elements by key function and return a Dict result.

    Args:
        on (Callable[[T], K]): Function to compute the key for grouping.

    Returns:
        Dict[K, list[T]]: Dict with grouped elements as lists.

    Example:
    ```python
    >>> import pyochain as pc
    >>> names = [
    ...     "Alice",
    ...     "Bob",
    ...     "Charlie",
    ...     "Dan",
    ...     "Edith",
    ...     "Frank",
    ... ]
    >>> pc.Iter(names).group_by(len).sort()
    ... # doctest: +NORMALIZE_WHITESPACE
    {3: ['Bob', 'Dan'], 5: ['Alice', 'Edith', 'Frank'], 7: ['Charlie']}
    >>>
    >>> iseven = lambda x: x % 2 == 0
    >>> pc.Iter([1, 2, 3, 4, 5, 6, 7, 8]).group_by(iseven)
    ... # doctest: +NORMALIZE_WHITESPACE
    {False: [1, 3, 5, 7], True: [2, 4, 6, 8]}

    ```
    Non-callable keys imply grouping on a member.
    ```python
    >>> data = [
    ...     {"name": "Alice", "gender": "F"},
    ...     {"name": "Bob", "gender": "M"},
    ...     {"name": "Charlie", "gender": "M"},
    ... ]
    >>> pc.Iter(data).group_by("gender").sort()
    ... # doctest: +NORMALIZE_WHITESPACE
    {'F': [{'gender': 'F', 'name': 'Alice'}],
    'M': [{'gender': 'M', 'name': 'Bob'}, {'gender': 'M', 'name': 'Charlie'}]}

    ```
    """
    from .._dict import Dict

    def _group_by(data: Iterable[T]) -> Dict[K, list[T]]:
        return Dict(cz.itertoolz.groupby(on, data))

    return self.into(_group_by)

inner

inner() -> T

Get the underlying data.

This is a terminal operation that ends the chain.

Returns:

Name Type Description
T T

The underlying data.

Source code in src/pyochain/_core/_main.py
105
106
107
108
109
110
111
112
113
def inner(self) -> T:
    """Get the underlying data.

    This is a terminal operation that ends the chain.

    Returns:
        T: The underlying data.
    """
    return self._inner

intersection

intersection(*others: Iterable[T]) -> Seq[T]

Return the elements common to this iterable and 'others'.

Is the opposite of difference.

See Also
  • difference
  • diff_symmetric
Note

This method consumes inner data, unsorts it, and removes duplicates.

Parameters:

Name Type Description Default
*others Iterable[T]

Other iterables to intersect with.

()

Returns:

Type Description
Seq[T]

Seq[T]: A new Seq containing the intersection of elements.

Example:

>>> import pyochain as pc
>>> pc.Seq([1, 2, 2]).intersection([2, 3], [2])
Seq(2,)

Source code in src/pyochain/_iter/_eager.py
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
def intersection(self, *others: Iterable[T]) -> Seq[T]:
    """Return the elements common to this iterable and 'others'.

    Is the opposite of `difference`.

    See Also:
        - `difference`
        - `diff_symmetric`

    Note:
        This method consumes inner data, unsorts it, and removes duplicates.

    Args:
        *others (Iterable[T]): Other iterables to intersect with.

    Returns:
        Seq[T]: A new Seq containing the intersection of elements.

    Example:
    ```python
    >>> import pyochain as pc
    >>> pc.Seq([1, 2, 2]).intersection([2, 3], [2])
    Seq(2,)

    ```
    """

    def _intersection(data: Iterable[T]) -> tuple[T, ...]:
        return tuple(set(data).intersection(*others))

    return self._eager(_intersection)

into

into(func: Callable[Concatenate[Self, P], R], *args: P.args, **kwargs: P.kwargs) -> R

Convert Self to R.

This method allows to pipe the instance into an object or function that can convert Self into another type.

Conceptually, this allow to do x.into(f) instead of f(x), hence keeping a functional chaining style.

This is a core method, shared by all pyochain wrappers, that allows chaining operations in a functional style.

Parameters:

Name Type Description Default
func Callable[Concatenate[Self, P], R]

Function for conversion.

required
*args P.args

Positional arguments to pass to the function.

()
**kwargs P.kwargs

Keyword arguments to pass to the function.

{}

Returns:

Name Type Description
R R

The converted value.

Example:

>>> import pyochain as pc
>>> def maybe_sum(data: pc.Seq[int]) -> pc.Option[int]:
...     match data.length():
...         case 0:
...             return pc.NONE
...         case _:
...             return pc.Some(data.sum())
>>>
>>> pc.Seq(range(5)).into(maybe_sum).unwrap()
10

Source code in src/pyochain/_core/_main.py
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
def into[**P, R](
    self,
    func: Callable[Concatenate[Self, P], R],
    *args: P.args,
    **kwargs: P.kwargs,
) -> R:
    """Convert `Self` to `R`.

    This method allows to pipe the instance into an object or function that can convert `Self` into another type.

    Conceptually, this allow to do x.into(f) instead of f(x), hence keeping a functional chaining style.

    This is a core method, shared by all pyochain wrappers, that allows chaining operations in a functional style.

    Args:
        func (Callable[Concatenate[Self, P], R]): Function for conversion.
        *args (P.args): Positional arguments to pass to the function.
        **kwargs (P.kwargs): Keyword arguments to pass to the function.

    Returns:
        R: The converted value.

    Example:
    ```python
    >>> import pyochain as pc
    >>> def maybe_sum(data: pc.Seq[int]) -> pc.Option[int]:
    ...     match data.length():
    ...         case 0:
    ...             return pc.NONE
    ...         case _:
    ...             return pc.Some(data.sum())
    >>>
    >>> pc.Seq(range(5)).into(maybe_sum).unwrap()
    10

    ```
    """
    return func(self, *args, **kwargs)

is_distinct

is_distinct() -> bool

Return True if all items are distinct.

Returns:

Name Type Description
bool bool

True if all items are distinct, False otherwise.

>>> import pyochain as pc
>>> pc.Seq([1, 2]).is_distinct()
True
Source code in src/pyochain/_iter/_main.py
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
def is_distinct(self) -> bool:
    """Return True if all items are distinct.

    Returns:
        bool: True if all items are distinct, False otherwise.

    ```python
    >>> import pyochain as pc
    >>> pc.Seq([1, 2]).is_distinct()
    True

    ```
    """
    return self.into(cz.itertoolz.isdistinct)

is_sorted

is_sorted(
    key: Callable[[T], U] | None = None, *, reverse: bool = False, strict: bool = False
) -> bool

Returns True if the items of iterable are in sorted order.

Parameters:

Name Type Description Default
key Callable[[T], U] | None

Function to transform items before comparison. Defaults to None.

None
reverse bool

Whether to check for descending order. Defaults to False.

False
strict bool

Whether to enforce strict sorting (no equal elements). Defaults to False.

False

Returns:

Name Type Description
bool bool

True if items are sorted according to the criteria, False otherwise.

Example:

>>> import pyochain as pc
>>> pc.Seq(["1", "2", "3", "4", "5"]).is_sorted(key=int)
True
>>> pc.Seq([5, 4, 3, 1, 2]).is_sorted(reverse=True)
False

If strict, tests for strict sorting, that is, returns False if equal elements are found:
```python
>>> pc.Seq([1, 2, 2]).is_sorted()
True
>>> pc.Seq([1, 2, 2]).is_sorted(strict=True)
False

The function returns False after encountering the first out-of-order item.

This means it may produce results that differ from the built-in sorted function for objects with unusual comparison dynamics (like math.nan).

If there are no out-of-order items, the iterable is exhausted.

Source code in src/pyochain/_iter/_booleans.py
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
def is_sorted[U](
    self,
    key: Callable[[T], U] | None = None,
    *,
    reverse: bool = False,
    strict: bool = False,
) -> bool:
    """Returns True if the items of iterable are in sorted order.

    Args:
        key (Callable[[T], U] | None): Function to transform items before comparison. Defaults to None.
        reverse (bool): Whether to check for descending order. Defaults to False.
        strict (bool): Whether to enforce strict sorting (no equal elements). Defaults to False.

    Returns:
        bool: True if items are sorted according to the criteria, False otherwise.

    Example:
    ```python
    >>> import pyochain as pc
    >>> pc.Seq(["1", "2", "3", "4", "5"]).is_sorted(key=int)
    True
    >>> pc.Seq([5, 4, 3, 1, 2]).is_sorted(reverse=True)
    False

    If strict, tests for strict sorting, that is, returns False if equal elements are found:
    ```python
    >>> pc.Seq([1, 2, 2]).is_sorted()
    True
    >>> pc.Seq([1, 2, 2]).is_sorted(strict=True)
    False

    ```

    The function returns False after encountering the first out-of-order item.

    This means it may produce results that differ from the built-in sorted function for objects with unusual comparison dynamics (like math.nan).

    If there are no out-of-order items, the iterable is exhausted.
    """
    return self.into(mit.is_sorted, key=key, reverse=reverse, strict=strict)

iter

iter() -> Iter[T]

Get an iterator over the sequence.

Call this to switch to lazy evaluation.

Returns:

Type Description
Iter[T]

Iter[T]: An Iter instance wrapping an iterator over the sequence.

Source code in src/pyochain/_iter/_main.py
1151
1152
1153
1154
1155
1156
1157
1158
1159
def iter(self) -> Iter[T]:
    """Get an iterator over the sequence.

    Call this to switch to lazy evaluation.

    Returns:
        Iter[T]: An `Iter` instance wrapping an iterator over the sequence.
    """
    return self._lazy(iter)

join

join(sep: str) -> str

Join all elements of the Iterable into a single string, with a specified separator.

Parameters:

Name Type Description Default
sep str

Separator to use between elements.

required

Returns:

Name Type Description
str str

The joined string.

Example:

>>> import pyochain as pc
>>> pc.Seq(["a", "b", "c"]).join("-")
'a-b-c'

Source code in src/pyochain/_iter/_aggregations.py
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
def join(self: IterWrapper[str], sep: str) -> str:
    """Join all elements of the `Iterable` into a single `string`, with a specified separator.

    Args:
        sep (str): Separator to use between elements.

    Returns:
        str: The joined string.

    Example:
    ```python
    >>> import pyochain as pc
    >>> pc.Seq(["a", "b", "c"]).join("-")
    'a-b-c'

    ```
    """
    return self.into(functools.partial(str.join, sep))

last

last() -> T

Return the last element.

Returns:

Name Type Description
T T

The last element of the iterable.

>>> import pyochain as pc
>>> pc.Seq([7, 8, 9]).last()
9
Source code in src/pyochain/_iter/_aggregations.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
def last(self) -> T:
    """Return the last element.

    Returns:
        T: The last element of the iterable.

    ```python
    >>> import pyochain as pc
    >>> pc.Seq([7, 8, 9]).last()
    9

    ```
    """
    return self.into(cz.itertoolz.last)

length

length() -> int

Return the length of the Iterable.

Like the builtin len but works on lazy sequences.

Returns:

Name Type Description
int int

The count of elements.

>>> import pyochain as pc
>>> pc.Seq([1, 2]).length()
2
Source code in src/pyochain/_iter/_aggregations.py
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
def length(self) -> int:
    """Return the length of the Iterable.

    Like the builtin len but works on lazy sequences.

    Returns:
        int: The count of elements.
    ```python
    >>> import pyochain as pc
    >>> pc.Seq([1, 2]).length()
    2

    ```
    """
    return self.into(cz.itertoolz.count)

max

max() -> U

Return the maximum of the sequence.

Returns:

Name Type Description
U U

The maximum value.

>>> import pyochain as pc
>>> pc.Seq([3, 1, 2]).max()
3
Source code in src/pyochain/_iter/_aggregations.py
306
307
308
309
310
311
312
313
314
315
316
317
318
319
def max[U: int | float](self: IterWrapper[U]) -> U:
    """Return the maximum of the sequence.

    Returns:
        U: The maximum value.

    ```python
    >>> import pyochain as pc
    >>> pc.Seq([3, 1, 2]).max()
    3

    ```
    """
    return self.into(max)

min

min() -> U

Return the minimum of the sequence.

Returns:

Name Type Description
U U

The minimum value.

>>> import pyochain as pc
>>> pc.Seq([3, 1, 2]).min()
1
Source code in src/pyochain/_iter/_aggregations.py
291
292
293
294
295
296
297
298
299
300
301
302
303
304
def min[U: int | float](self: IterWrapper[U]) -> U:
    """Return the minimum of the sequence.

    Returns:
        U: The minimum value.

    ```python
    >>> import pyochain as pc
    >>> pc.Seq([3, 1, 2]).min()
    1

    ```
    """
    return self.into(min)

most_common

most_common(n: int | None = None) -> Vec[tuple[T, int]]

Return the n most common elements and their counts.

If n is None, then all elements are returned.

Parameters:

Name Type Description Default
n int | None

Number of most common elements to return. Defaults to None (all elements).

None

Returns:

Type Description
Vec[tuple[T, int]]

Vec[tuple[T, int]]: A new Seq containing tuples of (element, count).

Example:

>>> import pyochain as pc
>>> pc.Seq([1, 1, 2, 3, 3, 3]).most_common(2)
Vec((3, 3), (1, 2))

Source code in src/pyochain/_iter/_eager.py
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
def most_common(self, n: int | None = None) -> Vec[tuple[T, int]]:
    """Return the n most common elements and their counts.

    If n is None, then all elements are returned.

    Args:
        n (int | None): Number of most common elements to return. Defaults to None (all elements).

    Returns:
        Vec[tuple[T, int]]: A new Seq containing tuples of (element, count).

    Example:
    ```python
    >>> import pyochain as pc
    >>> pc.Seq([1, 1, 2, 3, 3, 3]).most_common(2)
    Vec((3, 3), (1, 2))

    ```
    """
    from collections import Counter

    def _most_common(data: Iterable[T]) -> list[tuple[T, int]]:
        return Counter(data).most_common(n)

    return self._eager_mut(_most_common)

nth

nth(index: int) -> T

Return the nth item at index.

Parameters:

Name Type Description Default
index int

The index of the item to retrieve.

required

Returns:

Name Type Description
T T

The item at the specified index.

>>> import pyochain as pc
>>> pc.Seq([10, 20]).nth(1)
20
Source code in src/pyochain/_iter/_aggregations.py
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
def nth(self, index: int) -> T:
    """Return the nth item at index.

    Args:
        index (int): The index of the item to retrieve.

    Returns:
        T: The item at the specified index.

    ```python
    >>> import pyochain as pc
    >>> pc.Seq([10, 20]).nth(1)
    20

    ```
    """
    return self.into(functools.partial(cz.itertoolz.nth, index))

rearrange

rearrange(*indices: int) -> Vec[list[U]]

Rearrange elements in a given list of arrays by order indices.

The last element (value) always remains in place.

Parameters:

Name Type Description Default
*indices int

indices specifying new order of keys in each array.

()

Returns:

Type Description
Vec[list[U]]

Vec[list[U]]: A new Vec containing rearranged elements.

Example:

>>> import pyochain as pc
>>> data = pc.Seq([["A", "X", 1], ["A", "Y", 2], ["B", "X", 3], ["B", "Y", 4]])
>>> data.rearrange(1, 0)
Vec(['X', 'A', 1], ['Y', 'A', 2], ['X', 'B', 3], ['Y', 'B', 4])

Source code in src/pyochain/_iter/_eager.py
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
def rearrange[U: Sequence[Any]](self: BaseEager[U], *indices: int) -> Vec[list[U]]:
    """Rearrange elements in a given list of arrays by order indices.

    The last element (value) always remains in place.

    Args:
        *indices (int): indices specifying new order of keys in each array.

    Returns:
        Vec[list[U]]: A new Vec containing rearranged elements.

    Example:
    ```python
    >>> import pyochain as pc
    >>> data = pc.Seq([["A", "X", 1], ["A", "Y", 2], ["B", "X", 3], ["B", "Y", 4]])
    >>> data.rearrange(1, 0)
    Vec(['X', 'A', 1], ['Y', 'A', 2], ['X', 'B', 3], ['Y', 'B', 4])

    ```
    """

    def _check_bound(i: int, max_key_index: int) -> None:
        if i < 0 or i > max_key_index:
            msg = f"order index {i} out of range for row with {max_key_index + 1} keys"
            raise IndexError(
                msg,
            )

    def _rearrange(in_arrs: Iterable[U]) -> list[list[U]]:
        order = indices
        out: list[list[U]] = []
        for arr in in_arrs:
            max_key_index: int = len(arr) - 2
            for i in order:
                _check_bound(i, max_key_index)

            out.append([arr[i] for i in order] + [arr[-1]])

        return out

    return self._eager_mut(_rearrange)

reduce

reduce(func: Callable[[T, T], T]) -> T

Apply a function of two arguments cumulatively to the items of an iterable, from left to right.

Parameters:

Name Type Description Default
func Callable[[T, T], T]

Function to apply cumulatively to the items of the iterable.

required

Returns:

Name Type Description
T T

Single value resulting from cumulative reduction.

This effectively reduces the iterable to a single value.

If initial is present, it is placed before the items of the iterable in the calculation.

It then serves as a default when the iterable is empty.

>>> import pyochain as pc
>>> pc.Seq([1, 2, 3]).reduce(lambda a, b: a + b)
6

Source code in src/pyochain/_iter/_aggregations.py
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
def reduce(self, func: Callable[[T, T], T]) -> T:
    """Apply a function of two arguments cumulatively to the items of an iterable, from left to right.

    Args:
        func (Callable[[T, T], T]): Function to apply cumulatively to the items of the iterable.

    Returns:
        T: Single value resulting from cumulative reduction.

    This effectively reduces the iterable to a single value.

    If initial is present, it is placed before the items of the iterable in the calculation.

    It then serves as a default when the iterable is empty.
    ```python
    >>> import pyochain as pc
    >>> pc.Seq([1, 2, 3]).reduce(lambda a, b: a + b)
    6

    ```
    """

    def _reduce(data: Iterable[T]) -> T:
        return functools.reduce(func, data)

    return self.into(_reduce)

reduce_by

reduce_by(key: Callable[[T], K], binop: Callable[[T, T], T]) -> Dict[K, T]

Perform a simultaneous groupby and reduction.

Parameters:

Name Type Description Default
key Callable[[T], K]

Function to compute the key for grouping.

required
binop Callable[[T, T], T]

Binary operation to reduce the grouped elements.

required

Returns:

Type Description
Dict[K, T]

Dict[K, T]: Dict with grouped and reduced elements.

Example:

>>> from collections.abc import Iterable
>>> import pyochain as pc
>>> from operator import add, mul
>>>
>>> def is_even(x: int) -> bool:
...     return x % 2 == 0
>>>
>>> def group_reduce(data: Iterable[int]) -> int:
...     return pc.Iter(data).reduce(add)
>>>
>>> data = pc.Seq([1, 2, 3, 4, 5])
>>> data.iter().reduce_by(is_even, add)
{False: 9, True: 6}
>>> data.iter().group_by(is_even).map_values(group_reduce)
{False: 9, True: 6}
But the former does not build the intermediate groups, allowing it to operate in much less space.

This makes it suitable for larger datasets that do not fit comfortably in memory

Simple Examples:

>>> pc.Iter([1, 2, 3, 4, 5]).reduce_by(is_even, add)
{False: 9, True: 6}
>>> pc.Iter([1, 2, 3, 4, 5]).reduce_by(is_even, mul)
{False: 15, True: 8}

Source code in src/pyochain/_iter/_dicts.py
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
def reduce_by[K](
    self,
    key: Callable[[T], K],
    binop: Callable[[T, T], T],
) -> Dict[K, T]:
    """Perform a simultaneous groupby and reduction.

    Args:
        key (Callable[[T], K]): Function to compute the key for grouping.
        binop (Callable[[T, T], T]): Binary operation to reduce the grouped elements.

    Returns:
        Dict[K, T]: Dict with grouped and reduced elements.

    Example:
    ```python
    >>> from collections.abc import Iterable
    >>> import pyochain as pc
    >>> from operator import add, mul
    >>>
    >>> def is_even(x: int) -> bool:
    ...     return x % 2 == 0
    >>>
    >>> def group_reduce(data: Iterable[int]) -> int:
    ...     return pc.Iter(data).reduce(add)
    >>>
    >>> data = pc.Seq([1, 2, 3, 4, 5])
    >>> data.iter().reduce_by(is_even, add)
    {False: 9, True: 6}
    >>> data.iter().group_by(is_even).map_values(group_reduce)
    {False: 9, True: 6}

    ```
    But the former does not build the intermediate groups, allowing it to operate in much less space.

    This makes it suitable for larger datasets that do not fit comfortably in memory

    Simple Examples:
    ```python
    >>> pc.Iter([1, 2, 3, 4, 5]).reduce_by(is_even, add)
    {False: 9, True: 6}
    >>> pc.Iter([1, 2, 3, 4, 5]).reduce_by(is_even, mul)
    {False: 15, True: 8}

    ```
    """
    from .._dict import Dict

    def _reduce_by(data: Iterable[T]) -> Dict[K, T]:
        return Dict(cz.itertoolz.reduceby(key, binop, data))

    return self.into(_reduce_by)

second

second() -> T

Return the second element.

Returns:

Name Type Description
T T

The second element of the iterable.

>>> import pyochain as pc
>>> pc.Seq([9, 8]).second()
8
Source code in src/pyochain/_iter/_aggregations.py
143
144
145
146
147
148
149
150
151
152
153
154
155
156
def second(self) -> T:
    """Return the second element.

    Returns:
        T: The second element of the iterable.

    ```python
    >>> import pyochain as pc
    >>> pc.Seq([9, 8]).second()
    8

    ```
    """
    return self.into(cz.itertoolz.second)

sort

sort(key: Callable[[U], Any] | None = None, *, reverse: bool = False) -> Vec[U]

Sort the elements of the sequence.

Note

This method must consume the entire iterable to perform the sort. The result is a new Vec over the sorted sequence.

Parameters:

Name Type Description Default
key Callable[[U], Any] | None

Function to extract a comparison key from each element. Defaults to None.

None
reverse bool

Whether to sort in descending order. Defaults to False.

False

Returns:

Type Description
Vec[U]

Vec[U]: A Vec with elements sorted.

Example:

>>> import pyochain as pc
>>> pc.Seq([3, 1, 2]).sort()
Vec(1, 2, 3)

Source code in src/pyochain/_iter/_eager.py
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
def sort[U: SupportsRichComparison[Any]](
    self: BaseEager[U],
    key: Callable[[U], Any] | None = None,
    *,
    reverse: bool = False,
) -> Vec[U]:
    """Sort the elements of the sequence.

    Note:
        This method must consume the entire iterable to perform the sort.
        The result is a new `Vec` over the sorted sequence.

    Args:
        key (Callable[[U], Any] | None): Function to extract a comparison key from each element. Defaults to None.
        reverse (bool): Whether to sort in descending order. Defaults to False.

    Returns:
        Vec[U]: A `Vec` with elements sorted.

    Example:
    ```python
    >>> import pyochain as pc
    >>> pc.Seq([3, 1, 2]).sort()
    Vec(1, 2, 3)

    ```
    """

    def _sort(data: Iterable[U]) -> list[U]:
        return sorted(data, reverse=reverse, key=key)

    return self._eager_mut(_sort)

sum

sum() -> int

Return the sum of the sequence.

Returns:

Name Type Description
int int

The sum of all elements.

>>> import pyochain as pc
>>> pc.Seq([1, 2, 3]).sum()
6
Source code in src/pyochain/_iter/_aggregations.py
276
277
278
279
280
281
282
283
284
285
286
287
288
289
def sum[U: int | float](self: IterWrapper[U]) -> int:
    """Return the sum of the sequence.

    Returns:
        int: The sum of all elements.

    ```python
    >>> import pyochain as pc
    >>> pc.Seq([1, 2, 3]).sum()
    6

    ```
    """
    return self.into(sum)

tail

tail(n: int) -> Seq[T]

Return a tuple of the last n elements.

Parameters:

Name Type Description Default
n int

Number of elements to return.

required

Returns:

Type Description
Seq[T]

Seq[T]: A new Seq containing the last n elements.

Example:

>>> import pyochain as pc
>>> pc.Seq([1, 2, 3]).tail(2)
Seq(2, 3)

Source code in src/pyochain/_iter/_eager.py
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
def tail(self, n: int) -> Seq[T]:
    """Return a tuple of the last n elements.

    Args:
        n (int): Number of elements to return.

    Returns:
        Seq[T]: A new Seq containing the last n elements.

    Example:
    ```python
    >>> import pyochain as pc
    >>> pc.Seq([1, 2, 3]).tail(2)
    Seq(2, 3)

    ```
    """
    return self._eager(partial(cz.itertoolz.tail, n))

tap

tap(
    func: Callable[Concatenate[Self, P], Any], *args: P.args, **kwargs: P.kwargs
) -> Self

Tap into the chain to perform side effects without altering the data.

Parameters:

Name Type Description Default
func Callable[Concatenate[Self, P], Any]

Function to apply to the instance for side effects.

required
*args P.args

Positional arguments to pass to the function.

()
**kwargs P.kwargs

Keyword arguments to pass to the function.

{}

Returns:

Name Type Description
Self Self

The instance itself for chaining.

Example:

>>> import pyochain as pc
>>> pc.Seq([1, 2, 3, 4]).tap(print).last()
Seq(1, 2, 3, 4)
4

Source code in src/pyochain/_core/_main.py
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
def tap[**P](
    self,
    func: Callable[Concatenate[Self, P], Any],
    *args: P.args,
    **kwargs: P.kwargs,
) -> Self:
    """Tap into the chain to perform side effects without altering the data.

    Args:
        func (Callable[Concatenate[Self, P], Any]): Function to apply to the instance for side effects.
        *args (P.args): Positional arguments to pass to the function.
        **kwargs (P.kwargs): Keyword arguments to pass to the function.

    Returns:
        Self: The instance itself for chaining.

    Example:
    ```python
    >>> import pyochain as pc
    >>> pc.Seq([1, 2, 3, 4]).tap(print).last()
    Seq(1, 2, 3, 4)
    4

    ```
    """
    func(self, *args, **kwargs)
    return self

to_records

to_records() -> Dict[Any, Any]

Transform an iterable of nested sequences into a nested dictionary.

  • Each inner sequence represents a path to a value in the dictionary.
  • The last element of each sequence is treated as the value
  • All preceding elements are treated as keys leading to that value.

Returns:

Type Description
Dict[Any, Any]

Dict[Any, Any]: Nested dictionary constructed from the sequences.

Example:

>>> import pyochain as pc
>>> arrays = [["a", "b", 1], ["a", "c", 2], ["d", 3]]
>>> pc.Seq(arrays).to_records()
{'a': {'b': 1, 'c': 2}, 'd': 3}

Source code in src/pyochain/_iter/_dicts.py
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
def to_records[U: Sequence[Any]](self: BaseDict[U]) -> Dict[Any, Any]:
    """Transform an iterable of nested sequences into a nested dictionary.

    - Each inner sequence represents a path to a value in the dictionary.
    - The last element of each sequence is treated as the value
    - All preceding elements are treated as keys leading to that value.

    Returns:
        Dict[Any, Any]: Nested dictionary constructed from the sequences.

    Example:
    ```python
    >>> import pyochain as pc
    >>> arrays = [["a", "b", 1], ["a", "c", 2], ["d", 3]]
    >>> pc.Seq(arrays).to_records()
    {'a': {'b': 1, 'c': 2}, 'd': 3}

    ```
    """
    from .._dict import Dict

    def _from_nested(
        arrays: Iterable[Sequence[Any]],
        parent: dict[Any, Any] | None = None,
    ) -> dict[Any, Any]:
        d: dict[Any, Any] = parent or {}
        for arr in arrays:
            if len(arr) > 1:
                head, *tail = arr
                if len(tail) == 1:
                    d[head] = tail[0]
                else:
                    d[head] = _from_nested([tail], d.get(head, {}))
        return d

    return Dict(self.into(_from_nested))

top_n

top_n(n: int, key: Callable[[T], Any] | None = None) -> Seq[T]

Return a tuple of the top-n items according to key.

Parameters:

Name Type Description Default
n int

Number of top elements to return.

required
key Callable[[T], Any] | None

Function to extract a comparison key from each element. Defaults to None.

None

Returns:

Type Description
Seq[T]

Seq[T]: A new Seq containing the top-n elements.

Example:

>>> import pyochain as pc
>>> pc.Seq([1, 3, 2]).top_n(2)
Seq(3, 2)

Source code in src/pyochain/_iter/_eager.py
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
def top_n(self, n: int, key: Callable[[T], Any] | None = None) -> Seq[T]:
    """Return a tuple of the top-n items according to key.

    Args:
        n (int): Number of top elements to return.
        key (Callable[[T], Any] | None): Function to extract a comparison key from each element. Defaults to None.

    Returns:
        Seq[T]: A new Seq containing the top-n elements.

    Example:
    ```python
    >>> import pyochain as pc
    >>> pc.Seq([1, 3, 2]).top_n(2)
    Seq(3, 2)

    ```
    """
    return self._eager(partial(cz.itertoolz.topk, n, key=key))

union

union(*others: Iterable[T]) -> Seq[T]

Return the union of this iterable and 'others'.

Note

This method consumes inner data and removes duplicates.

Parameters:

Name Type Description Default
*others Iterable[T]

Other iterables to include in the union.

()

Returns:

Type Description
Seq[T]

Seq[T]: A new Seq containing the union of elements.

Example:

>>> import pyochain as pc
>>> pc.Seq([1, 2, 2]).union([2, 3], [4]).iter().sort()
Vec(1, 2, 3, 4)

Source code in src/pyochain/_iter/_eager.py
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
def union(self, *others: Iterable[T]) -> Seq[T]:
    """Return the union of this iterable and 'others'.

    Note:
        This method consumes inner data and removes duplicates.

    Args:
        *others (Iterable[T]): Other iterables to include in the union.

    Returns:
        Seq[T]: A new Seq containing the union of elements.

    Example:
    ```python
    >>> import pyochain as pc
    >>> pc.Seq([1, 2, 2]).union([2, 3], [4]).iter().sort()
    Vec(1, 2, 3, 4)

    ```
    """

    def _union(data: Iterable[T]) -> tuple[T, ...]:
        return tuple(set(data).union(*others))

    return self._eager(_union)

unzip

unzip() -> Unzipped[U, V]

Converts an iterator of pairs into a pair of iterators.

Returns:

Type Description
Unzipped[U, V]

Unzipped[U, V]: dataclass with first and second iterators.

Iter.unzip() consumes the iterator of pairs.

Returns an Unzipped dataclass, containing two iterators:

  • one from the left elements of the pairs
  • one from the right elements.

This function is, in some sense, the opposite of zip.

>>> import pyochain as pc
>>> data = [(1, "a"), (2, "b"), (3, "c")]
>>> unzipped = pc.Seq(data).unzip()
>>> unzipped.left.collect()
Seq(1, 2, 3)
>>> unzipped.right.collect()
Seq('a', 'b', 'c')

Source code in src/pyochain/_iter/_aggregations.py
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
def unzip[U, V](self: IterWrapper[tuple[U, V]]) -> Unzipped[U, V]:
    """Converts an iterator of pairs into a pair of iterators.

    Returns:
        Unzipped[U, V]: dataclass with first and second iterators.

    `Iter.unzip()` consumes the iterator of pairs.

    Returns an Unzipped dataclass, containing two iterators:

    - one from the left elements of the pairs
    - one from the right elements.

    This function is, in some sense, the opposite of zip.
    ```python
    >>> import pyochain as pc
    >>> data = [(1, "a"), (2, "b"), (3, "c")]
    >>> unzipped = pc.Seq(data).unzip()
    >>> unzipped.left.collect()
    Seq(1, 2, 3)
    >>> unzipped.right.collect()
    Seq('a', 'b', 'c')

    ```
    """
    from ._main import Iter

    def _unzip(data: Iterable[tuple[U, V]]) -> Unzipped[U, V]:
        d: tuple[tuple[U, V], ...] = tuple(data)
        return Unzipped(Iter(x[0] for x in d), Iter(x[1] for x in d))

    return self.into(_unzip)

with_keys

with_keys(keys: Iterable[K]) -> Dict[K, T]

Create a Dict by zipping the iterable with keys.

Parameters:

Name Type Description Default
keys Iterable[K]

Iterable of keys to pair with the values.

required

Returns:

Type Description
Dict[K, T]

Dict[K, T]: Dict with the provided keys and iterable values.

Example:

>>> import pyochain as pc
>>> keys = ["a", "b", "c"]
>>> values = [1, 2, 3]
>>> pc.Seq(values).iter().with_keys(keys)
{'a': 1, 'b': 2, 'c': 3}
>>> # This is equivalent to:
>>> pc.Iter(keys).zip(values).into(lambda x: pc.Dict(dict(x)))
{'a': 1, 'b': 2, 'c': 3}

Source code in src/pyochain/_iter/_dicts.py
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
def with_keys[K](self, keys: Iterable[K]) -> Dict[K, T]:
    """Create a Dict by zipping the iterable with keys.

    Args:
        keys (Iterable[K]): Iterable of keys to pair with the values.

    Returns:
        Dict[K, T]: Dict with the provided keys and iterable values.

    Example:
    ```python
    >>> import pyochain as pc
    >>> keys = ["a", "b", "c"]
    >>> values = [1, 2, 3]
    >>> pc.Seq(values).iter().with_keys(keys)
    {'a': 1, 'b': 2, 'c': 3}
    >>> # This is equivalent to:
    >>> pc.Iter(keys).zip(values).into(lambda x: pc.Dict(dict(x)))
    {'a': 1, 'b': 2, 'c': 3}

    ```
    """
    from .._dict import Dict

    def _with_keys(data: Iterable[T]) -> Dict[K, T]:
        return Dict(dict(zip(keys, data, strict=False)))

    return self.into(_with_keys)

with_values

with_values(values: Iterable[V]) -> Dict[T, V]

Create a Dict by zipping the iterable with values.

Parameters:

Name Type Description Default
values Iterable[V]

Iterable of values to pair with the keys.

required

Returns:

Type Description
Dict[T, V]

Dict[T, V]: Dict with the iterable as keys and provided values.

Example:

>>> import pyochain as pc
>>> keys = [1, 2, 3]
>>> values = ["a", "b", "c"]
>>> pc.Iter(keys).with_values(values)
{1: 'a', 2: 'b', 3: 'c'}
>>> # This is equivalent to:
>>> pc.Iter(keys).zip(values).into(lambda x: pc.Dict(dict(x)))
{1: 'a', 2: 'b', 3: 'c'}

Source code in src/pyochain/_iter/_dicts.py
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
def with_values[V](self, values: Iterable[V]) -> Dict[T, V]:
    """Create a Dict by zipping the iterable with values.

    Args:
        values (Iterable[V]): Iterable of values to pair with the keys.

    Returns:
        Dict[T, V]: Dict with the iterable as keys and provided values.

    Example:
    ```python
    >>> import pyochain as pc
    >>> keys = [1, 2, 3]
    >>> values = ["a", "b", "c"]
    >>> pc.Iter(keys).with_values(values)
    {1: 'a', 2: 'b', 3: 'c'}
    >>> # This is equivalent to:
    >>> pc.Iter(keys).zip(values).into(lambda x: pc.Dict(dict(x)))
    {1: 'a', 2: 'b', 3: 'c'}

    ```
    """
    from .._dict import Dict

    def _with_values(data: Iterable[T]) -> Dict[T, V]:
        return Dict(dict(zip(data, values, strict=False)))

    return self.into(_with_values)