Skip to content

Commit

Permalink
Merge branch 'kalekundert-master'
Browse files Browse the repository at this point in the history
  • Loading branch information
bbayles committed May 31, 2020
2 parents 3c1e75e + 7c08fc9 commit a7a5b9a
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.rst
Expand Up @@ -42,6 +42,7 @@ Python iterables.
| Augmenting | `count_cycle <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.count_cycle>`_, |
| | `intersperse <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.intersperse>`_, |
| | `padded <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.padded>`_, |
| | `mark_ends <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.mark_ends>`_, |
| | `repeat_last <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.repeat_last>`_, |
| | `adjacent <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.adjacent>`_, |
| | `groupby_transform <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.groupby_transform>`_, |
Expand Down
1 change: 1 addition & 0 deletions docs/api.rst
Expand Up @@ -82,6 +82,7 @@ These tools yield items from an iterable, plus additional data.
.. autofunction:: count_cycle
.. autofunction:: intersperse
.. autofunction:: padded
.. autofunction:: mark_ends
.. autofunction:: repeat_last
.. autofunction:: adjacent
.. autofunction:: groupby_transform
Expand Down
25 changes: 25 additions & 0 deletions more_itertools/more.py
Expand Up @@ -37,6 +37,7 @@
'consecutive_groups',
'consumer',
'count_cycle',
'mark_ends',
'difference',
'distinct_combinations',
'distinct_permutations',
Expand Down Expand Up @@ -2025,6 +2026,30 @@ def count_cycle(iterable, n=None):
return ((i, item) for i in counter for item in iterable)


def mark_ends(iterable):
"""Yield the items in *iterable*, along with two booleans indicating
whether or not each element is the first and/or last.
>>> list(mark_ends('ABC'))
[(True, False, 'A'), (False, False, 'B'), (False, True, 'C')]
"""
it = iter(iterable)

try:
b = next(it)
except StopIteration:
return

try:
for i in count():
a = b
b = next(it)
yield i == 0, False, a

except StopIteration:
yield i == 0, True, a


def locate(iterable, pred=bool, window_size=None):
"""Yield the index of each item in *iterable* for which *pred* returns
``True``.
Expand Down
3 changes: 3 additions & 0 deletions more_itertools/more.pyi
Expand Up @@ -272,6 +272,9 @@ class numeric_range(Generic[_T, _U], Sequence[_T], Hashable, Reversible[_T]):
def count_cycle(
iterable: Iterable[_T], n: Optional[int] = ...
) -> Iterable[Tuple[int, _T]]: ...
def mark_ends(
iterable: Iterable[_T],
) -> Iterable[Tuple[bool, bool, _T]]: ...
def locate(
iterable: Iterable[object],
pred: Callable[..., Any] = ...,
Expand Down
41 changes: 41 additions & 0 deletions tests/test_more.py
Expand Up @@ -2803,6 +2803,47 @@ def test_negative(self):
self.assertEqual(list(mi.count_cycle('abc', -3)), [])


class MarkEndsTests(TestCase):
def test_basic(self):

def generator():
yield from [0, 1, 2, 3]

cases = [{
'input': [],
'output': [],
}, {
'input': [0],
'output': [(True, True, 0)],
}, {
'input': [0, 1],
'output': [(True, False, 0), (False, True, 1)],
}, {
'input': [0, 1, 2],
'output': [(True, False, 0), (False, False, 1), (False, True, 2)],
}, {
'input': [0, 1, 2, 3],
'output': [
(True, False, 0),
(False, False, 1),
(False, False, 2),
(False, True, 3),
],
}, {
'input': generator(), # No `__len__()`
'output': [
(True, False, 0),
(False, False, 1),
(False, False, 2),
(False, True, 3),
],
}]

for case in cases:
actual = list(mi.mark_ends(case['input']))
self.assertEqual(actual, case['output'])


class LocateTests(TestCase):
def test_default_pred(self):
iterable = [0, 1, 1, 0, 1, 0, 0]
Expand Down

0 comments on commit a7a5b9a

Please sign in to comment.