Skip to content

Commit ea20841

Browse files
authored
Add filterfalse to itertools (#109)
1 parent 7b3b9bc commit ea20841

File tree

4 files changed

+55
-0
lines changed

4 files changed

+55
-0
lines changed

asyncstdlib/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
chain,
2626
compress,
2727
dropwhile,
28+
filterfalse,
2829
islice,
2930
takewhile,
3031
starmap,
@@ -71,6 +72,7 @@
7172
"chain",
7273
"compress",
7374
"dropwhile",
75+
"filterfalse",
7476
"takewhile",
7577
"islice",
7678
"starmap",

asyncstdlib/itertools.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,27 @@ async def dropwhile(
242242
yield item
243243

244244

245+
async def filterfalse(
246+
predicate: Union[Callable[[T], bool], Callable[[T], Awaitable[bool]], None],
247+
iterable: AnyIterable[T],
248+
) -> AsyncIterator[T]:
249+
"""
250+
Yield items from ``iterable`` for which ``predicate(item)`` is false.
251+
252+
If ``predicate`` is ``None``, return items which are false.
253+
254+
Lazily iterates over ``iterable``, yielding only items for which
255+
``predicate`` of the current item is false.
256+
"""
257+
async with ScopedIter(iterable) as async_iter:
258+
if predicate is None:
259+
predicate = bool
260+
predicate = _awaitify(predicate)
261+
async for item in async_iter:
262+
if not await predicate(item):
263+
yield item
264+
265+
245266
async def islice(iterable: AnyIterable[T], *args: Optional[int]) -> AsyncIterator[T]:
246267
"""
247268
An :term:`asynchronous iterator` over items from ``iterable`` in a :py:class:`slice`

docs/source/api/itertools.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ Iterator filtering
4343
.. autofunction:: dropwhile(predicate: (T) → (await) bool, iterable: (async) iter T)
4444
:async-for: :T
4545

46+
.. autofunction:: filterfalse(predicate: None | (T) → (await) bool, iterable: (async) iter T)
47+
:async-for: :T
48+
4649
.. autofunction:: takewhile(predicate: (T) → (await) bool, iterable: (async) iter T)
4750
:async-for: :T
4851

unittests/test_itertools.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,35 @@ async def test_dropwhile(iterable, predicate):
178178
)
179179

180180

181+
filterfalse_cases = (
182+
(lambda x: True, [0, 1] * 5),
183+
(lambda x: False, [0, 1] * 5),
184+
(lambda x: x, [0, 1] * 5),
185+
(lambda x: x < 5, range(20)),
186+
(lambda x: x > 5, range(20)),
187+
)
188+
189+
190+
@pytest.mark.parametrize("predicate, iterable", filterfalse_cases)
191+
@sync
192+
async def test_filterfalse(predicate, iterable):
193+
expected = list(itertools.filterfalse(predicate, iterable))
194+
assert await a.list(a.filterfalse(predicate, iterable)) == expected
195+
assert await a.list(a.filterfalse(awaitify(predicate), iterable)) == expected
196+
assert await a.list(a.filterfalse(predicate, asyncify(iterable))) == expected
197+
assert (
198+
await a.list(a.filterfalse(awaitify(predicate), asyncify(iterable))) == expected
199+
)
200+
201+
202+
@pytest.mark.parametrize("predicate, iterable", filterfalse_cases)
203+
@sync
204+
async def test_filterfalse_predicate_none(predicate, iterable):
205+
expected = list(itertools.filterfalse(None, iterable))
206+
assert await a.list(a.filterfalse(None, iterable)) == expected
207+
assert await a.list(a.filterfalse(None, asyncify(iterable))) == expected
208+
209+
181210
@pytest.mark.parametrize("iterable, predicate", droptakewhile_cases)
182211
@sync
183212
async def test_takewhile(iterable, predicate):

0 commit comments

Comments
 (0)