Add more_itertools module with `take` and `chunked`
This commit is contained in:
parent
4ee12ea16f
commit
bfc15b2ee6
|
@ -72,8 +72,8 @@ async for value in islice(generator1(), 2, None, 2):
|
|||
```
|
||||
|
||||
|
||||
See [builtins.py][] and [itertools.py][] for full documentation
|
||||
of functions and abilities.
|
||||
See [builtins.py][], [itertools.py][], and [more_itertools.py][] for full
|
||||
documentation of functions and abilities.
|
||||
|
||||
|
||||
License
|
||||
|
|
|
@ -340,7 +340,7 @@ async def islice(itr: AnyIterable[T], *args: Optional[int]) -> AsyncIterator[T]:
|
|||
if not args:
|
||||
raise ValueError("must pass stop index")
|
||||
if len(args) == 1:
|
||||
stop, = args
|
||||
(stop,) = args
|
||||
elif len(args) == 2:
|
||||
start, stop = args # type: ignore
|
||||
elif len(args) == 3:
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
# Copyright 2020 John Reese
|
||||
# Licensed under the MIT license
|
||||
|
||||
from typing import AsyncIterable, List, TypeVar
|
||||
|
||||
from .builtins import iter
|
||||
from .itertools import islice
|
||||
from .types import AnyIterable
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
async def take(n: int, iterable: AnyIterable[T]) -> List[T]:
|
||||
"""
|
||||
Return the first n items of iterable as a list.
|
||||
|
||||
If there are too few items in iterable, all of them are returned.
|
||||
n needs to be at least 0. If it is 0, an empty list is returned.
|
||||
|
||||
Example:
|
||||
|
||||
first_two = await take(2, [1, 2, 3, 4, 5])
|
||||
|
||||
"""
|
||||
if n < 0:
|
||||
raise ValueError("take's first parameter can't be negative")
|
||||
return [item async for item in islice(iterable, n)]
|
||||
|
||||
|
||||
async def chunked(iterable: AnyIterable[T], n: int) -> AsyncIterable[List[T]]:
|
||||
"""
|
||||
Break iterable into chunks of length n.
|
||||
|
||||
The last chunk will be shorter if the total number of items is not
|
||||
divisible by n.
|
||||
|
||||
Example:
|
||||
|
||||
async for chunk in chunked([1, 2, 3, 4, 5], n=2):
|
||||
... # first iteration: chunk == [1, 2]; last one: chunk == [5]
|
||||
"""
|
||||
it = iter(iterable)
|
||||
chunk = await take(n, it)
|
||||
while chunk != []:
|
||||
yield chunk
|
||||
chunk = await take(n, it)
|
|
@ -5,3 +5,4 @@ from .asyncio import AsyncioTest
|
|||
from .builtins import BuiltinsTest
|
||||
from .helpers import HelpersTest
|
||||
from .itertools import ItertoolsTest
|
||||
from .more_itertools import MoreItertoolsTest
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
# Copyright 2020 John Reese
|
||||
# Licensed under the MIT license
|
||||
|
||||
from typing import AsyncIterable
|
||||
from unittest import TestCase
|
||||
|
||||
import aioitertools.more_itertools as mit
|
||||
|
||||
from .helpers import async_test
|
||||
|
||||
|
||||
async def _gen() -> AsyncIterable[int]:
|
||||
for i in range(5):
|
||||
yield i
|
||||
|
||||
|
||||
async def _empty() -> AsyncIterable[int]:
|
||||
return
|
||||
yield 0 # pylint: disable=unreachable
|
||||
|
||||
|
||||
class MoreItertoolsTest(TestCase):
|
||||
@async_test
|
||||
async def test_take(self) -> None:
|
||||
self.assertEqual(await mit.take(2, _gen()), [0, 1])
|
||||
self.assertEqual(await mit.take(2, range(5)), [0, 1])
|
||||
|
||||
@async_test
|
||||
async def test_take_zero(self) -> None:
|
||||
self.assertEqual(await mit.take(0, _gen()), [])
|
||||
|
||||
@async_test
|
||||
async def test_take_negative(self) -> None:
|
||||
with self.assertRaises(ValueError):
|
||||
await mit.take(-1, _gen())
|
||||
|
||||
@async_test
|
||||
async def test_take_more_than_iterable(self) -> None:
|
||||
self.assertEqual(await mit.take(10, _gen()), list(range(5)))
|
||||
|
||||
@async_test
|
||||
async def test_take_empty(self) -> None:
|
||||
it = _gen()
|
||||
self.assertEqual(len(await mit.take(5, it)), 5)
|
||||
self.assertEqual(await mit.take(1, it), [])
|
||||
self.assertEqual(await mit.take(1, _empty()), [])
|
||||
|
||||
@async_test
|
||||
async def test_chunked(self) -> None:
|
||||
self.assertEqual(
|
||||
[chunk async for chunk in mit.chunked(_gen(), 2)], [[0, 1], [2, 3], [4]]
|
||||
)
|
||||
self.assertEqual(
|
||||
[chunk async for chunk in mit.chunked(range(5), 2)], [[0, 1], [2, 3], [4]]
|
||||
)
|
||||
|
||||
@async_test
|
||||
async def test_chunked_empty(self) -> None:
|
||||
self.assertEqual([], [chunk async for chunk in mit.chunked(_empty(), 2)])
|
Loading…
Reference in New Issue