diff --git a/src/humanize/__init__.py b/src/humanize/__init__.py index 6fb9959..715111e 100644 --- a/src/humanize/__init__.py +++ b/src/humanize/__init__.py @@ -4,7 +4,7 @@ from humanize.filesize import naturalsize from humanize.i18n import activate, deactivate, decimal_separator, thousands_separator -from humanize.lists import natural_list +from humanize.lists import natural_list, natural_list_etc from humanize.number import ( apnumber, clamp, @@ -37,6 +37,7 @@ "intword", "metric", "natural_list", + "natural_list_etc", "naturaldate", "naturalday", "naturaldelta", diff --git a/src/humanize/lists.py b/src/humanize/lists.py index 3a52f1e..ae507db 100644 --- a/src/humanize/lists.py +++ b/src/humanize/lists.py @@ -6,7 +6,7 @@ if TYPE_CHECKING: from typing import Any -__all__ = ["natural_list"] +__all__ = ["natural_list", "natural_list_etc"] def natural_list(items: list[Any]) -> str: @@ -34,3 +34,34 @@ def natural_list(items: list[Any]) -> str: return f"{str(items[0])} and {str(items[1])}" else: return ", ".join(str(item) for item in items[:-1]) + f" and {str(items[-1])}" + + +def natural_list_etc(items: list[Any], limit: int) -> str: + """Natural list. + + Convert a list of items into a human-readable string with commas and 'etc.' + + Examples: + >>> natural_list_etc(["one", "two", "three", "four", "five"], 4) + 'one, two, three, four, etc.' + >>> natural_list_etc(["one", "two", "three"], 4) + 'one, two and three' + >>> natural_list_etc(["one", "two"], 3) + 'one and two' + >>> natural_list_etc(["one"], 2) + 'one' + + Args: + items (list): An iterable of items. + limit (int): limitation of items when "etc" is appeared in the end. + + Returns: + str: A string with commas and 'and' in the right places. + """ + leng_of_items = len(items) + if leng_of_items == limit or leng_of_items < limit: + return natural_list(items) + else: + return ( + ", ".join(str(item) for item in items[: limit - leng_of_items]) + ", etc." + ) diff --git a/tests/test_lists.py b/tests/test_lists.py index a39d344..5eddfa0 100644 --- a/tests/test_lists.py +++ b/tests/test_lists.py @@ -21,3 +21,25 @@ def test_natural_list( test_args: list[str] | list[int] | list[str | int], expected: str ) -> None: assert humanize.natural_list(*test_args) == expected + + +@pytest.mark.parametrize( + "test_args, expected", + [ + ( + [["one", "two", "three", "four", "five", "six", "seven", "eight"], 6], + "one, two, three, four, five, six, etc.", + ), + ([["1", "2", "3"], 4], "1, 2 and 3"), + ([["one", "two", "three"], 4], "one, two and three"), + ([["one", "two"], 3], "one and two"), + ([["one"], 3], "one"), + ([[""], 3], ""), + ([[1, 2, 3], 4], "1, 2 and 3"), + ([[1, "two"], 4], "1 and two"), + ], +) +def test_natural_list_etc( + test_args: list[str] | list[int] | list[str | int], expected: str +) -> None: + assert humanize.natural_list_etc(*test_args) == expected