From eac6be0f4c852645867e762963134cfebbe7983d Mon Sep 17 00:00:00 2001 From: Saturday-boyi <2174084306@qq.com> Date: Sun, 15 Nov 2026 08:10:05 +0800 Subject: [PATCH] Stop nth after reading the requested item --- CHANGELOG.rst | 1 + src/pydash/arrays.py | 4 +++- tests/test_arrays.py | 15 +++++++++++++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index cc415a7b..38edfd1c 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -6,6 +6,7 @@ Changelog Unreleased ---------- +- Stop consuming iterables in ``nth`` once a nonnegative index has been reached. - Stop ``partial`` and ``partial_right`` wrappers from accepting a positional iteratee argument in a slot that a keyword argument already binds, so ``map_`` passes the element instead of an index into the bound parameter. diff --git a/src/pydash/arrays.py b/src/pydash/arrays.py index 9fd8d5d9..8b157c9d 100644 --- a/src/pydash/arrays.py +++ b/src/pydash/arrays.py @@ -1204,12 +1204,14 @@ def nth(array: t.Iterable[T], pos: int = 0) -> t.Union[T, None]: .. versionchanged:: VERSION Support iterables that are not sequences, such as generators and sets. - They previously returned ``None``. + For a nonnegative index, iteration stops once the requested element is found. """ # pyd.get indexes by position, which needs a sequence. A mapping is left # alone because it is indexed by key rather than position, and realizing # one would look up its keys instead. if not isinstance(array, (Sequence, Mapping)): + if isinstance(pos, int) and pos >= 0: + return next((value for index, value in enumerate(array) if index == pos), None) array = list(array) return pyd.get(array, pos) diff --git a/tests/test_arrays.py b/tests/test_arrays.py index 50c8d536..66f85e1c 100644 --- a/tests/test_arrays.py +++ b/tests/test_arrays.py @@ -685,6 +685,21 @@ def test_nth_with_non_sequence_iterables(factory, pos, expected): assert _.nth(factory(), pos) == expected +@parametrize("pos", [0, 2]) +def test_nth_only_consumes_through_the_requested_position(pos): + def values(): + yield from range(pos + 1) + pytest.fail("nth consumed an item after the requested position") + + assert _.nth(values(), pos) == pos + + +def test_nth_leaves_remaining_items_in_the_iterator(): + values = iter([11, 22, 33, 44]) + assert _.nth(values, 1) == 22 + assert list(values) == [33, 44] + + @parametrize( "case,pos,expected", [({0: "x", 1: "y"}, 0, "x"), ({0: "x", 1: "y"}, 1, "y"), ({"a": 1}, 0, None)],