Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions src/humanize/lists.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,25 @@
__all__ = ["natural_list"]


def natural_list(items: Iterable[Any]) -> str:
def natural_list(items: Iterable[Any], oxford_comma: bool = False) -> str:
"""Natural list.

Convert an iterable of items into a human-readable string with commas and 'and'.

Examples:
>>> natural_list(["one", "two", "three"])
'one, two and three'
>>> natural_list(["one", "two", "three"], oxford_comma=True)
'one, two, and three'
>>> natural_list(["one", "two"])
'one and two'
>>> natural_list(["one"])
'one'

Args:
items (Iterable): An iterable of items.
oxford_comma (bool): Include a comma before 'and' for three or more items.
Defaults to False.

Returns:
str: A string with commas and 'and' in the right places.
Expand All @@ -37,4 +41,5 @@ def natural_list(items: Iterable[Any]) -> str:
elif len(item_list) == 2:
return f"{item_list[0]} and {item_list[1]}"
else:
return ", ".join(item_list[:-1]) + f" and {item_list[-1]}"
separator = ", and " if oxford_comma else " and "
return ", ".join(item_list[:-1]) + separator + item_list[-1]
23 changes: 23 additions & 0 deletions tests/test_lists.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,26 @@
)
def test_natural_list(test_args: Iterable[Any], expected: str) -> None:
assert humanize.natural_list(*test_args) == expected


@pytest.mark.parametrize("oxford_comma", [False, True])
@pytest.mark.parametrize(
"items, without_comma, with_comma",
[
([], "", ""),
(["one"], "one", "one"),
(["one", "two"], "one and two", "one and two"),
(["one", "two", "three"], "one, two and three", "one, two, and three"),
([1, 2, 3, 4], "1, 2, 3 and 4", "1, 2, 3, and 4"),
],
)
def test_natural_list_oxford_comma(
items: list[Any], without_comma: str, with_comma: str, oxford_comma: bool
) -> None:
expected = with_comma if oxford_comma else without_comma
assert humanize.natural_list(items, oxford_comma=oxford_comma) == expected


def test_natural_list_oxford_comma_generator() -> None:
items = (item for item in ["one", "two", "three"])
assert humanize.natural_list(items, oxford_comma=True) == "one, two, and three"