diff --git a/CHANGELOG.md b/CHANGELOG.md index 77c4058..09c67bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Release 1.6 +- Added [parametrize](https://pypi.org/project/parametrize/) for parameterized unit tests - Fixed tests failing (to find test files) when running from the IDE or the terminal when not in the right directory - Added Python version to GitHub Action workflow job steps and set Black to show required formatting changes - Upgraded pre-commit hooks (pre-commit-hooks to `v5.0.0` and ruff-pre-commit to `v0.6.0`) @@ -9,6 +10,10 @@ - Added support for Python 3.12 and 3.13 by upgrading Pylint and disabling/fixing Pylint errors - Corrected and improved language consistency in [readme](README.md) and `CHANGELOG.md` +### New Features + +- Implemented `zip_with_next` function + ## Release 1.5 ## Release 1.4 diff --git a/README.md b/README.md index 7e02ecb..f36b5dc 100644 --- a/README.md +++ b/README.md @@ -366,6 +366,7 @@ complete documentation reference | `tails()` | Returns consecutive tails of sequence | transformation | | `zip(other)` | Zips the sequence with `other` | transformation | | `zip_with_index(start=0)` | Zips the sequence with the index starting at `start` on the right side | transformation | +| `zip_with_next()` | Zips the sequence minus last element with itself minus first element, resulting in adjacent elements paired with each other | transformation | | `enumerate(start=0)` | Zips the sequence with the index starting at `start` on the left side | transformation | | `cartesian(*iterables, repeat=1)` | Returns cartesian product from itertools.product | transformation | | `inner_join(other)` | Returns inner join of sequence with `other`. Must be a sequence of `(key, value)` pairs | transformation | diff --git a/functional/pipeline.py b/functional/pipeline.py index d923994..77a9822 100644 --- a/functional/pipeline.py +++ b/functional/pipeline.py @@ -1394,6 +1394,18 @@ def zip_with_index(self, start: int = 0) -> Sequence[tuple[_T_co, int]]: """ return self._transform(transformations.zip_with_index_t(start)) + def zip_with_next(self) -> Sequence[tuple[_T_co, _T_co]]: + """ + Zips this sequence minus last element with itself minus first element, + resulting in sequence of each element paired with the next. + + >>> seq([1, 2, 3, 4]).zip_with_next() + [(1, 2), (2, 3), (3, 4)] + + :return: sequence of adjacent elements paired + """ + return self._transform(transformations.zip_with_next_t()) + def enumerate(self, start: int = 0) -> Sequence[tuple[int, _T_co]]: """ Uses python enumerate to to zip the sequence with indexes starting at start. diff --git a/functional/test/test_functional.py b/functional/test/test_functional.py index ea5f47e..ab3b4a5 100644 --- a/functional/test/test_functional.py +++ b/functional/test/test_functional.py @@ -8,6 +8,8 @@ from functional.transformations import name from functional import seq, pseq +from parametrize import parametrize # type: ignore + Data = namedtuple("Data", "x y") @@ -831,6 +833,19 @@ def test_zip_with_index(self): self.assertIteratorEqual(result, e) self.assert_type(result) + @parametrize( + "sequence,expected", + [ + ([1, 2, 3, 4], [(1, 2), (2, 3), (3, 4)]), + ([i for i in range(10_000)], [(i, i + 1) for i in range(10_000 - 1)]), + ([], []), + ], + ) + def test_zip_with_next(self, sequence, expected): + result = self.seq(sequence).zip_with_next() + self.assertIteratorEqual(result, expected) + self.assert_type(result) + def test_to_list(self): l = [1, 2, 3, "abc", {1: 2}, {1, 2, 3}] result = self.seq(l).to_list() diff --git a/functional/test/test_type.py b/functional/test/test_type.py index d7c6982..5abb4f4 100644 --- a/functional/test/test_type.py +++ b/functional/test/test_type.py @@ -174,6 +174,10 @@ def type_checking() -> None: ["a", "b", "c"] ).zip_with_index() + t_zip_with_next: Sequence[tuple[str, str]] = seq( + ["a", "b", "c"] + ).zip_with_next() + t_enumerate: Sequence[tuple[int, str]] = seq(["a", "b", "c"]).enumerate(start=1) # t_inner_join: Sequence[tuple[str, Sequence[int]]] = seq([('a', 1), ('b', 2), ('c', 3)]).inner_join([('a', 2), ('c', 5)]) diff --git a/functional/transformations.py b/functional/transformations.py index 0920471..0809ac9 100644 --- a/functional/transformations.py +++ b/functional/transformations.py @@ -307,6 +307,16 @@ def zip_with_index_t(start): ) +def zip_with_next_t(): + """ + Transformation for Sequence.zip_with_next + :return: transformation + """ + return Transformation( + "zip_with_next", lambda sequence: zip(sequence, sequence[1:]), None + ) + + def enumerate_t(start): """ Transformation for Sequence.enumerate diff --git a/poetry.lock b/poetry.lock index 243ff1a..2259239 100644 --- a/poetry.lock +++ b/poetry.lock @@ -433,6 +433,18 @@ files = [ numpy = {version = ">=1.25.0", markers = "python_version >= \"3.9\""} types-pytz = ">=2022.1.1" +[[package]] +name = "parametrize" +version = "0.1.1" +description = "Drop-in @pytest.mark.parametrize replacement working with unittest.TestCase" +optional = false +python-versions = ">=3.6.2,<4.0.0" +groups = ["dev"] +files = [ + {file = "parametrize-0.1.1-py3-none-any.whl", hash = "sha256:618fc00d15a03df7177691e83e59aeb976b20c410ce39af5063d1839a4673645"}, + {file = "parametrize-0.1.1.tar.gz", hash = "sha256:d7ac0f61b781d1eadfa81d9e57ea80d5e184078e1976b7bb052ab682d9ef35de"}, +] + [[package]] name = "pathspec" version = "0.12.1" @@ -687,4 +699,4 @@ all = ["pandas"] [metadata] lock-version = "2.1" python-versions = "^3.9.0" -content-hash = "de928370fbf75434bdf0422126d9eb7445e765daac804d0cabab5649fb12d737" +content-hash = "1ad7e4e1a128c4f392fa2a2095f7881b91610fa029006ac21333c09ab0ef873f" diff --git a/pyproject.toml b/pyproject.toml index d79f56f..40790c3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -39,6 +39,7 @@ all = ["pandas"] [tool.poetry.group.dev.dependencies] black = "^23.1" +parametrize = "^0.1.1" pytest = "^7.3.1" pylint = "^3.3.3" pytest-cov = "^4.0.0"