pyochain 0.5.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of pyochain might be problematic. Click here for more details.
- pyochain/__init__.py +5 -0
- pyochain/_core/__init__.py +21 -0
- pyochain/_core/_main.py +184 -0
- pyochain/_core/_protocols.py +43 -0
- pyochain/_dict/__init__.py +4 -0
- pyochain/_dict/_exprs.py +115 -0
- pyochain/_dict/_filters.py +273 -0
- pyochain/_dict/_funcs.py +62 -0
- pyochain/_dict/_groups.py +176 -0
- pyochain/_dict/_iter.py +92 -0
- pyochain/_dict/_joins.py +137 -0
- pyochain/_dict/_main.py +307 -0
- pyochain/_dict/_nested.py +218 -0
- pyochain/_dict/_process.py +171 -0
- pyochain/_iter/__init__.py +3 -0
- pyochain/_iter/_aggregations.py +323 -0
- pyochain/_iter/_booleans.py +224 -0
- pyochain/_iter/_constructors.py +155 -0
- pyochain/_iter/_eager.py +195 -0
- pyochain/_iter/_filters.py +503 -0
- pyochain/_iter/_groups.py +264 -0
- pyochain/_iter/_joins.py +407 -0
- pyochain/_iter/_lists.py +306 -0
- pyochain/_iter/_main.py +224 -0
- pyochain/_iter/_maps.py +358 -0
- pyochain/_iter/_partitions.py +148 -0
- pyochain/_iter/_process.py +384 -0
- pyochain/_iter/_rolling.py +247 -0
- pyochain/_iter/_tuples.py +221 -0
- pyochain/py.typed +0 -0
- pyochain-0.5.0.dist-info/METADATA +295 -0
- pyochain-0.5.0.dist-info/RECORD +33 -0
- pyochain-0.5.0.dist-info/WHEEL +4 -0
pyochain/_iter/_maps.py
ADDED
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import itertools
|
|
4
|
+
from collections.abc import Callable, Collection, Generator, Iterable, Iterator, Mapping
|
|
5
|
+
from functools import partial
|
|
6
|
+
from typing import TYPE_CHECKING, Any, Concatenate, Self, overload
|
|
7
|
+
|
|
8
|
+
import cytoolz as cz
|
|
9
|
+
import more_itertools as mit
|
|
10
|
+
|
|
11
|
+
from .._core import IterWrapper
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from ._main import Iter
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class BaseMap[T](IterWrapper[T]):
|
|
18
|
+
def for_each[**P](
|
|
19
|
+
self,
|
|
20
|
+
func: Callable[Concatenate[T, P], Any],
|
|
21
|
+
*args: P.args,
|
|
22
|
+
**kwargs: P.kwargs,
|
|
23
|
+
) -> Self:
|
|
24
|
+
"""
|
|
25
|
+
Apply a function to each element in the iterable.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
func: Function to apply to each element.
|
|
29
|
+
args: Positional arguments for the function.
|
|
30
|
+
kwargs: Keyword arguments for the function.
|
|
31
|
+
|
|
32
|
+
Can be used for side effects such as printing or logging.
|
|
33
|
+
```python
|
|
34
|
+
>>> import pyochain as pc
|
|
35
|
+
>>> pc.Iter.from_([1, 2, 3]).for_each(lambda x: print(x)).collect().unwrap()
|
|
36
|
+
1
|
|
37
|
+
2
|
|
38
|
+
3
|
|
39
|
+
[]
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
"""
|
|
43
|
+
for v in self.unwrap():
|
|
44
|
+
func(v, *args, **kwargs)
|
|
45
|
+
return self
|
|
46
|
+
|
|
47
|
+
def map[R](self, func: Callable[[T], R]) -> Iter[R]:
|
|
48
|
+
"""
|
|
49
|
+
Map each element through func and return a Iter of results.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
func: Function to apply to each element.
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
>>> import pyochain as pc
|
|
56
|
+
>>> pc.Iter.from_([1, 2]).map(lambda x: x + 1).into(list)
|
|
57
|
+
[2, 3]
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
"""
|
|
61
|
+
return self.apply(partial(map, func))
|
|
62
|
+
|
|
63
|
+
@overload
|
|
64
|
+
def flat_map[U, R](
|
|
65
|
+
self: IterWrapper[Iterable[Iterable[Iterable[U]]]],
|
|
66
|
+
func: Callable[[T], Iterable[Iterable[R]]],
|
|
67
|
+
) -> Iter[Iterable[Iterable[R]]]: ...
|
|
68
|
+
@overload
|
|
69
|
+
def flat_map[U, R](
|
|
70
|
+
self: IterWrapper[Iterable[Iterable[U]]], func: Callable[[T], Iterable[R]]
|
|
71
|
+
) -> Iter[Iterable[R]]: ...
|
|
72
|
+
@overload
|
|
73
|
+
def flat_map[U, R](
|
|
74
|
+
self: IterWrapper[Iterable[U]], func: Callable[[T], R]
|
|
75
|
+
) -> Iter[R]: ...
|
|
76
|
+
def flat_map[U: Iterable[Any], R](
|
|
77
|
+
self: IterWrapper[U], func: Callable[[T], R]
|
|
78
|
+
) -> Iter[Any]:
|
|
79
|
+
"""
|
|
80
|
+
Map each element through func and flatten the result by one level.
|
|
81
|
+
|
|
82
|
+
Args:
|
|
83
|
+
func: Function to apply to each element.
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
>>> import pyochain as pc
|
|
87
|
+
>>> data = [[1, 2], [3, 4]]
|
|
88
|
+
>>> pc.Iter.from_(data).flat_map(lambda x: x + 10).into(list)
|
|
89
|
+
[11, 12, 13, 14]
|
|
90
|
+
|
|
91
|
+
```
|
|
92
|
+
"""
|
|
93
|
+
|
|
94
|
+
def _flat_map(data: Iterable[U]) -> map[R]:
|
|
95
|
+
return map(func, itertools.chain.from_iterable(data))
|
|
96
|
+
|
|
97
|
+
return self.apply(_flat_map)
|
|
98
|
+
|
|
99
|
+
def map_star[U: Iterable[Any], R](
|
|
100
|
+
self: IterWrapper[U], func: Callable[..., R]
|
|
101
|
+
) -> Iter[R]:
|
|
102
|
+
"""
|
|
103
|
+
Applies a function to each element, where each element is an iterable.
|
|
104
|
+
|
|
105
|
+
Args:
|
|
106
|
+
func: Function to apply to unpacked elements.
|
|
107
|
+
|
|
108
|
+
Unlike `.map()`, which passes each element as a single argument, `.starmap()` unpacks each element into positional arguments for the function.
|
|
109
|
+
|
|
110
|
+
In short, for each `element` in the sequence, it computes `func(*element)`.
|
|
111
|
+
```python
|
|
112
|
+
>>> import pyochain as pc
|
|
113
|
+
>>> def make_sku(color, size):
|
|
114
|
+
... return f"{color}-{size}"
|
|
115
|
+
>>> data = pc.Seq(["blue", "red"])
|
|
116
|
+
>>> data.iter().product(["S", "M"]).map_star(make_sku).into(list)
|
|
117
|
+
['blue-S', 'blue-M', 'red-S', 'red-M']
|
|
118
|
+
|
|
119
|
+
```
|
|
120
|
+
This is equivalent to:
|
|
121
|
+
```python
|
|
122
|
+
>>> data.iter().product(["S", "M"]).map(lambda x: make_sku(*x)).into(list)
|
|
123
|
+
['blue-S', 'blue-M', 'red-S', 'red-M']
|
|
124
|
+
|
|
125
|
+
```
|
|
126
|
+
- Use map_star when the performance matters (it is faster).
|
|
127
|
+
- Use map with unpacking when readability matters (the types can be inferred).
|
|
128
|
+
"""
|
|
129
|
+
|
|
130
|
+
return self.apply(partial(itertools.starmap, func))
|
|
131
|
+
|
|
132
|
+
def map_if[R](
|
|
133
|
+
self,
|
|
134
|
+
predicate: Callable[[T], bool],
|
|
135
|
+
func: Callable[[T], R],
|
|
136
|
+
func_else: Callable[[T], R] | None = None,
|
|
137
|
+
) -> Iter[R]:
|
|
138
|
+
"""
|
|
139
|
+
Evaluate each item from iterable using pred.
|
|
140
|
+
|
|
141
|
+
Args:
|
|
142
|
+
predicate: Function to evaluate each item.
|
|
143
|
+
func: Function to apply if predicate is True.
|
|
144
|
+
func_else: Function to apply if predicate is False.
|
|
145
|
+
|
|
146
|
+
- If the result is equivalent to True, transform the item with func and yield it.
|
|
147
|
+
- Otherwise, transform the item with func_else and yield it.
|
|
148
|
+
- Predicate, func, and func_else should each be functions that accept one argument.
|
|
149
|
+
|
|
150
|
+
By default, func_else is the identity function.
|
|
151
|
+
```python
|
|
152
|
+
>>> import pyochain as pc
|
|
153
|
+
>>> from math import sqrt
|
|
154
|
+
>>> iterable = pc.Iter.from_(range(-5, 5)).collect()
|
|
155
|
+
>>> iterable.into(list)
|
|
156
|
+
[-5, -4, -3, -2, -1, 0, 1, 2, 3, 4]
|
|
157
|
+
>>> iterable.iter().map_if(lambda x: x > 3, lambda x: "toobig").into(list)
|
|
158
|
+
[-5, -4, -3, -2, -1, 0, 1, 2, 3, 'toobig']
|
|
159
|
+
>>> iterable.iter().map_if(
|
|
160
|
+
... lambda x: x >= 0,
|
|
161
|
+
... lambda x: f"{sqrt(x):.2f}",
|
|
162
|
+
... lambda x: None,
|
|
163
|
+
... ).into(list)
|
|
164
|
+
[None, None, None, None, None, '0.00', '1.00', '1.41', '1.73', '2.00']
|
|
165
|
+
|
|
166
|
+
```
|
|
167
|
+
"""
|
|
168
|
+
return self.apply(mit.map_if, predicate, func, func_else=func_else)
|
|
169
|
+
|
|
170
|
+
def map_except[R](
|
|
171
|
+
self, func: Callable[[T], R], *exceptions: type[BaseException]
|
|
172
|
+
) -> Iter[R]:
|
|
173
|
+
"""
|
|
174
|
+
Transform each item from iterable with function and yield the result, unless function raises one of the specified exceptions.
|
|
175
|
+
|
|
176
|
+
Args:
|
|
177
|
+
func: Function to apply to each item.
|
|
178
|
+
exceptions: Exceptions to catch and ignore.
|
|
179
|
+
|
|
180
|
+
The function is called to transform each item in iterable
|
|
181
|
+
|
|
182
|
+
If an exception other than one given by exceptions is raised by function, it is raised like normal.
|
|
183
|
+
```python
|
|
184
|
+
>>> import pyochain as pc
|
|
185
|
+
>>> iterable = ["1", "2", "three", "4", None]
|
|
186
|
+
>>> pc.Iter.from_(iterable).map_except(int, ValueError, TypeError).into(list)
|
|
187
|
+
[1, 2, 4]
|
|
188
|
+
|
|
189
|
+
```
|
|
190
|
+
"""
|
|
191
|
+
|
|
192
|
+
def _map_except(data: Iterable[T]) -> Iterator[R]:
|
|
193
|
+
return mit.map_except(func, data, *exceptions)
|
|
194
|
+
|
|
195
|
+
return self.apply(_map_except)
|
|
196
|
+
|
|
197
|
+
def repeat(
|
|
198
|
+
self, n: int, factory: Callable[[Iterable[T]], Collection[T]] = tuple
|
|
199
|
+
) -> Iter[Iterable[T]]:
|
|
200
|
+
"""
|
|
201
|
+
Repeat the entire iterable n times (as elements) and return Iter.
|
|
202
|
+
|
|
203
|
+
Args:
|
|
204
|
+
n: Number of repetitions.
|
|
205
|
+
factory: Factory to create the repeated collection (default: tuple).
|
|
206
|
+
|
|
207
|
+
```python
|
|
208
|
+
>>> import pyochain as pc
|
|
209
|
+
>>> pc.Iter.from_([1, 2]).repeat(2).collect().unwrap()
|
|
210
|
+
[(1, 2), (1, 2)]
|
|
211
|
+
>>> pc.Iter.from_([1, 2]).repeat(3, list).collect().unwrap()
|
|
212
|
+
[[1, 2], [1, 2], [1, 2]]
|
|
213
|
+
|
|
214
|
+
```
|
|
215
|
+
"""
|
|
216
|
+
|
|
217
|
+
def _repeat(data: Iterable[T]) -> Iterator[Iterable[T]]:
|
|
218
|
+
return itertools.repeat(factory(data), n)
|
|
219
|
+
|
|
220
|
+
return self.apply(_repeat)
|
|
221
|
+
|
|
222
|
+
@overload
|
|
223
|
+
def repeat_last(self, default: T) -> Iter[T]: ...
|
|
224
|
+
@overload
|
|
225
|
+
def repeat_last[U](self, default: U) -> Iter[T | U]: ...
|
|
226
|
+
def repeat_last[U](self, default: U = None) -> Iter[T | U]:
|
|
227
|
+
"""
|
|
228
|
+
After the iterable is exhausted, keep yielding its last element.
|
|
229
|
+
|
|
230
|
+
**Warning** ⚠️
|
|
231
|
+
This creates an infinite iterator.
|
|
232
|
+
Be sure to use `Iter.take()` or `Iter.slice()` to limit the number of items taken.
|
|
233
|
+
|
|
234
|
+
Args:
|
|
235
|
+
default: Value to yield if the iterable is empty.
|
|
236
|
+
|
|
237
|
+
Example:
|
|
238
|
+
```python
|
|
239
|
+
>>> import pyochain as pc
|
|
240
|
+
>>> pc.Iter.from_(range(3)).repeat_last().take(5).into(list)
|
|
241
|
+
[0, 1, 2, 2, 2]
|
|
242
|
+
|
|
243
|
+
If the iterable is empty, yield default forever:
|
|
244
|
+
```python
|
|
245
|
+
>>> pc.Iter.from_(range(0)).repeat_last(42).take(5).into(list)
|
|
246
|
+
[42, 42, 42, 42, 42]
|
|
247
|
+
|
|
248
|
+
```
|
|
249
|
+
"""
|
|
250
|
+
return self.apply(mit.repeat_last, default)
|
|
251
|
+
|
|
252
|
+
def ichunked(self, n: int) -> Iter[Iterator[T]]:
|
|
253
|
+
"""
|
|
254
|
+
|
|
255
|
+
Break *iterable* into sub-iterables with *n* elements each.
|
|
256
|
+
|
|
257
|
+
Args:
|
|
258
|
+
n: Number of elements in each chunk.
|
|
259
|
+
|
|
260
|
+
If the sub-iterables are read in order, the elements of *iterable*
|
|
261
|
+
won't be stored in memory.
|
|
262
|
+
|
|
263
|
+
If they are read out of order, :func:`itertools.tee` is used to cache
|
|
264
|
+
elements as necessary.
|
|
265
|
+
```python
|
|
266
|
+
>>> import pyochain as pc
|
|
267
|
+
>>> all_chunks = pc.Iter.from_count().ichunked(4).unwrap()
|
|
268
|
+
>>> c_1, c_2, c_3 = next(all_chunks), next(all_chunks), next(all_chunks)
|
|
269
|
+
>>> list(c_2) # c_1's elements have been cached; c_3's haven't been
|
|
270
|
+
[4, 5, 6, 7]
|
|
271
|
+
>>> list(c_1)
|
|
272
|
+
[0, 1, 2, 3]
|
|
273
|
+
>>> list(c_3)
|
|
274
|
+
[8, 9, 10, 11]
|
|
275
|
+
|
|
276
|
+
```
|
|
277
|
+
"""
|
|
278
|
+
return self.apply(mit.ichunked, n)
|
|
279
|
+
|
|
280
|
+
@overload
|
|
281
|
+
def flatten[U](
|
|
282
|
+
self: IterWrapper[Iterable[Iterable[Iterable[U]]]],
|
|
283
|
+
) -> Iter[Iterable[Iterable[U]]]: ...
|
|
284
|
+
@overload
|
|
285
|
+
def flatten[U](self: IterWrapper[Iterable[Iterable[U]]]) -> Iter[Iterable[U]]: ...
|
|
286
|
+
@overload
|
|
287
|
+
def flatten[U](self: IterWrapper[Iterable[U]]) -> Iter[U]: ...
|
|
288
|
+
def flatten(self: IterWrapper[Iterable[Any]]) -> Iter[Any]:
|
|
289
|
+
"""
|
|
290
|
+
Flatten one level of nesting and return a new Iterable wrapper.
|
|
291
|
+
|
|
292
|
+
This is a shortcut for `.apply(itertools.chain.from_iterable)`.
|
|
293
|
+
```python
|
|
294
|
+
>>> import pyochain as pc
|
|
295
|
+
>>> pc.Iter.from_([[1, 2], [3]]).flatten().into(list)
|
|
296
|
+
[1, 2, 3]
|
|
297
|
+
|
|
298
|
+
```
|
|
299
|
+
"""
|
|
300
|
+
return self.apply(itertools.chain.from_iterable)
|
|
301
|
+
|
|
302
|
+
def pluck[U: Mapping[Any, Any]](
|
|
303
|
+
self: IterWrapper[U], *keys: str | int
|
|
304
|
+
) -> Iter[Any]:
|
|
305
|
+
"""
|
|
306
|
+
Get an element from each item in a sequence using a nested key path.
|
|
307
|
+
|
|
308
|
+
Args:
|
|
309
|
+
keys: Nested keys to extract values.
|
|
310
|
+
|
|
311
|
+
```python
|
|
312
|
+
>>> import pyochain as pc
|
|
313
|
+
>>> data = pc.Seq(
|
|
314
|
+
... [
|
|
315
|
+
... {"id": 1, "info": {"name": "Alice", "age": 30}},
|
|
316
|
+
... {"id": 2, "info": {"name": "Bob", "age": 25}},
|
|
317
|
+
... ]
|
|
318
|
+
... )
|
|
319
|
+
>>> data.iter().pluck("info").into(list)
|
|
320
|
+
[{'name': 'Alice', 'age': 30}, {'name': 'Bob', 'age': 25}]
|
|
321
|
+
>>> data.iter().pluck("info", "name").into(list)
|
|
322
|
+
['Alice', 'Bob']
|
|
323
|
+
|
|
324
|
+
```
|
|
325
|
+
Example: get the maximum age along with the corresponding id)
|
|
326
|
+
```python
|
|
327
|
+
>>> data.iter().pluck("info", "age").zip(
|
|
328
|
+
... data.iter().pluck("id").into(list)
|
|
329
|
+
... ).max()
|
|
330
|
+
(30, 1)
|
|
331
|
+
|
|
332
|
+
```
|
|
333
|
+
"""
|
|
334
|
+
|
|
335
|
+
getter = partial(cz.dicttoolz.get_in, keys)
|
|
336
|
+
return self.apply(partial(map, getter))
|
|
337
|
+
|
|
338
|
+
def round[U: float | int](
|
|
339
|
+
self: IterWrapper[U], ndigits: int | None = None
|
|
340
|
+
) -> Iter[float]:
|
|
341
|
+
"""
|
|
342
|
+
Round each element in the iterable to the given number of decimal places and return Iter.
|
|
343
|
+
|
|
344
|
+
Args:
|
|
345
|
+
ndigits: Number of decimal places to round to.
|
|
346
|
+
|
|
347
|
+
```python
|
|
348
|
+
>>> import pyochain as pc
|
|
349
|
+
>>> pc.Iter.from_([1.2345, 2.3456, 3.4567]).round(2).into(list)
|
|
350
|
+
[1.23, 2.35, 3.46]
|
|
351
|
+
|
|
352
|
+
```
|
|
353
|
+
"""
|
|
354
|
+
|
|
355
|
+
def _round(data: Iterable[U]) -> Generator[float | int, None, None]:
|
|
356
|
+
return (round(x, ndigits) for x in data)
|
|
357
|
+
|
|
358
|
+
return self.apply(_round)
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import itertools
|
|
4
|
+
from collections.abc import Callable
|
|
5
|
+
from functools import partial
|
|
6
|
+
from typing import TYPE_CHECKING, Literal, overload
|
|
7
|
+
|
|
8
|
+
import cytoolz as cz
|
|
9
|
+
|
|
10
|
+
from .._core import IterWrapper
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from ._main import Iter
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class BasePartitions[T](IterWrapper[T]):
|
|
17
|
+
@overload
|
|
18
|
+
def windows(self, length: Literal[1]) -> Iter[tuple[T]]: ...
|
|
19
|
+
@overload
|
|
20
|
+
def windows(self, length: Literal[2]) -> Iter[tuple[T, T]]: ...
|
|
21
|
+
@overload
|
|
22
|
+
def windows(self, length: Literal[3]) -> Iter[tuple[T, T, T]]: ...
|
|
23
|
+
@overload
|
|
24
|
+
def windows(self, length: Literal[4]) -> Iter[tuple[T, T, T, T]]: ...
|
|
25
|
+
@overload
|
|
26
|
+
def windows(self, length: Literal[5]) -> Iter[tuple[T, T, T, T, T]]: ...
|
|
27
|
+
|
|
28
|
+
def windows(self, length: int) -> Iter[tuple[T, ...]]:
|
|
29
|
+
"""
|
|
30
|
+
A sequence of overlapping subsequences of the given length.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
length: The length of each window.
|
|
34
|
+
|
|
35
|
+
```python
|
|
36
|
+
>>> import pyochain as pc
|
|
37
|
+
>>> pc.Iter.from_([1, 2, 3, 4]).windows(2).into(list)
|
|
38
|
+
[(1, 2), (2, 3), (3, 4)]
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
This function allows you to apply custom function not available in the rolling namespace.
|
|
42
|
+
```python
|
|
43
|
+
>>> def moving_average(seq: tuple[int, ...]) -> float:
|
|
44
|
+
... return float(sum(seq)) / len(seq)
|
|
45
|
+
>>> pc.Iter.from_([1, 2, 3, 4]).windows(2).map(moving_average).into(list)
|
|
46
|
+
[1.5, 2.5, 3.5]
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
"""
|
|
50
|
+
return self.apply(partial(cz.itertoolz.sliding_window, length))
|
|
51
|
+
|
|
52
|
+
@overload
|
|
53
|
+
def partition(self, n: Literal[1], pad: None = None) -> Iter[tuple[T]]: ...
|
|
54
|
+
@overload
|
|
55
|
+
def partition(self, n: Literal[2], pad: None = None) -> Iter[tuple[T, T]]: ...
|
|
56
|
+
@overload
|
|
57
|
+
def partition(self, n: Literal[3], pad: None = None) -> Iter[tuple[T, T, T]]: ...
|
|
58
|
+
@overload
|
|
59
|
+
def partition(self, n: Literal[4], pad: None = None) -> Iter[tuple[T, T, T, T]]: ...
|
|
60
|
+
@overload
|
|
61
|
+
def partition(
|
|
62
|
+
self, n: Literal[5], pad: None = None
|
|
63
|
+
) -> Iter[tuple[T, T, T, T, T]]: ...
|
|
64
|
+
@overload
|
|
65
|
+
def partition(self, n: int, pad: int) -> Iter[tuple[T, ...]]: ...
|
|
66
|
+
def partition(self, n: int, pad: int | None = None) -> Iter[tuple[T, ...]]:
|
|
67
|
+
"""
|
|
68
|
+
Partition sequence into tuples of length n.
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
n: Length of each partition.
|
|
72
|
+
pad: Value to pad the last partition if needed.
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
>>> import pyochain as pc
|
|
76
|
+
>>> pc.Iter.from_([1, 2, 3, 4]).partition(2).into(list)
|
|
77
|
+
[(1, 2), (3, 4)]
|
|
78
|
+
|
|
79
|
+
```
|
|
80
|
+
If the length of seq is not evenly divisible by n, the final tuple is dropped if pad is not specified, or filled to length n by pad:
|
|
81
|
+
```python
|
|
82
|
+
>>> pc.Iter.from_([1, 2, 3, 4, 5]).partition(2).into(list)
|
|
83
|
+
[(1, 2), (3, 4), (5, None)]
|
|
84
|
+
|
|
85
|
+
```
|
|
86
|
+
"""
|
|
87
|
+
|
|
88
|
+
return self.apply(partial(cz.itertoolz.partition, n, pad=pad))
|
|
89
|
+
|
|
90
|
+
def partition_all(self, n: int) -> Iter[tuple[T, ...]]:
|
|
91
|
+
"""
|
|
92
|
+
Partition all elements of sequence into tuples of length at most n.
|
|
93
|
+
|
|
94
|
+
Args:
|
|
95
|
+
n: Maximum length of each partition.
|
|
96
|
+
|
|
97
|
+
The final tuple may be shorter to accommodate extra elements.
|
|
98
|
+
```python
|
|
99
|
+
>>> import pyochain as pc
|
|
100
|
+
>>> pc.Iter.from_([1, 2, 3, 4]).partition_all(2).into(list)
|
|
101
|
+
[(1, 2), (3, 4)]
|
|
102
|
+
>>> pc.Iter.from_([1, 2, 3, 4, 5]).partition_all(2).into(list)
|
|
103
|
+
[(1, 2), (3, 4), (5,)]
|
|
104
|
+
|
|
105
|
+
```
|
|
106
|
+
"""
|
|
107
|
+
return self.apply(partial(cz.itertoolz.partition_all, n))
|
|
108
|
+
|
|
109
|
+
def partition_by(self, predicate: Callable[[T], bool]) -> Iter[tuple[T, ...]]:
|
|
110
|
+
"""
|
|
111
|
+
Partition the `iterable` into a sequence of `tuples` according to a predicate function.
|
|
112
|
+
|
|
113
|
+
Args:
|
|
114
|
+
predicate: Function to determine partition boundaries.
|
|
115
|
+
|
|
116
|
+
Every time the output of `predicate` changes, a new `tuple` is started,
|
|
117
|
+
and subsequent items are collected into that `tuple`.
|
|
118
|
+
```python
|
|
119
|
+
>>> import pyochain as pc
|
|
120
|
+
>>> pc.Iter.from_("I have space").partition_by(lambda c: c == " ").into(list)
|
|
121
|
+
[('I',), (' ',), ('h', 'a', 'v', 'e'), (' ',), ('s', 'p', 'a', 'c', 'e')]
|
|
122
|
+
>>>
|
|
123
|
+
>>> data = [1, 2, 1, 99, 88, 33, 99, -1, 5]
|
|
124
|
+
>>> pc.Iter.from_(data).partition_by(lambda x: x > 10).into(list)
|
|
125
|
+
[(1, 2, 1), (99, 88, 33, 99), (-1, 5)]
|
|
126
|
+
|
|
127
|
+
```
|
|
128
|
+
"""
|
|
129
|
+
return self.apply(partial(cz.recipes.partitionby, predicate))
|
|
130
|
+
|
|
131
|
+
def batch(self, n: int) -> Iter[tuple[T, ...]]:
|
|
132
|
+
"""
|
|
133
|
+
Batch elements into tuples of length n and return a new Iter.
|
|
134
|
+
|
|
135
|
+
Args:
|
|
136
|
+
n: Number of elements in each batch.
|
|
137
|
+
|
|
138
|
+
- The last batch may be shorter than n.
|
|
139
|
+
- The data is consumed lazily, just enough to fill a batch.
|
|
140
|
+
- The result is yielded as soon as a batch is full or when the input iterable is exhausted.
|
|
141
|
+
```python
|
|
142
|
+
>>> import pyochain as pc
|
|
143
|
+
>>> pc.Iter.from_("ABCDEFG").batch(3).into(list)
|
|
144
|
+
[('A', 'B', 'C'), ('D', 'E', 'F'), ('G',)]
|
|
145
|
+
|
|
146
|
+
```
|
|
147
|
+
"""
|
|
148
|
+
return self.apply(itertools.batched, n)
|