Grammatically correct human readable string from list (with Oxford comma)
Asked Answered
A

4

6

I want a grammatically correct human-readable string representation of a list. For example, the list ['A', 2, None, 'B,B', 'C,C,C'] should return the string A, 2, None, B,B, and C,C,C. This contrived example is somewhat necessary. Note that the Oxford comma is relevant for this question.

I tried ', '.join(seq) but this doesn't produce the expected result for the aforementioned example.

Note the preexisting similar questions:

Anthrax answered 30/12, 2018 at 22:11 Comment(0)
A
14

This function works by handling small lists differently than larger lists.

from typing import Any, List

def readable_list(seq: List[Any]) -> str:
    """Return a grammatically correct human readable string (with an Oxford comma)."""
    # Ref: https://stackoverflow.com/a/53981846/
    seq = [str(s) for s in seq]
    if len(seq) < 3:
        return ' and '.join(seq)
    return ', '.join(seq[:-1]) + ', and ' + seq[-1]

Usage examples:

readable_list([])
''

readable_list(['A'])
'A'

readable_list(['A', 2])
'A and 2'

readable_list(['A', None, 'C'])
'A, None, and C'

readable_list(['A', 'B,B', 'C,C,C'])
'A, B,B, and C,C,C'

readable_list(['A', 'B', 'C', 'D'])
'A, B, C, and D'
Anthrax answered 30/12, 2018 at 22:11 Comment(0)
D
2

You can also use unpacking for a slightly cleaner solution:

def readable_list(_s):
  if len(_s) < 3:
    return ' and '.join(map(str, _s))
  *a, b = _s
  return f"{', '.join(map(str, a))}, and {b}"

vals = [[], ['A'], ['A', 2], ['A', None, 'C'], ['A', 'B,B', 'C,C,C'], ['A', 'B', 'C', 'D']]
print([readable_list(i) for i in vals])

Output:

['', 'A', 'A and 2', 'A, None, and C', 'A, B,B, and C,C,C', 'A, B, C, and D']
Donahoe answered 30/12, 2018 at 22:14 Comment(0)
R
0

I got really stubborn and I really wanted to figure out a one-liner solution.

"{} and {}".format(seq[0], seq[1]) if len(seq)==2 else ', '.join([str(x) if (y < len(seq)-1 or len(seq)<=1) else "and {}".format(str(x)) for x, y in zip(seq, range(len(seq)))])

I think this one does the trick. And I think the problem is also more complicated than I thought to be solved with a non-ugly one-liner.

Rebecarebecca answered 19/6, 2019 at 13:40 Comment(6)
Huh? Why do you say that? I think the command does what I think OP wanted to doRebecarebecca
Sorry I misunderstood, I retracted the flag. all is fine :)Bombast
@GiacomoCasoni Given seq = [1, 2], the returned value should grammatically be '1 and 2', but this answer returns '1, and 2' instead which is not correct.Anthrax
This answer should either be fixed (while maintaining its originality) or be deleted. Being fixed is preferable.Anthrax
Oh, I must have misunderstood what the Oxford comma is. Let me have a look if I can change it upRebecarebecca
You might be curious to take a look at another attempt of a one-liner solution that I wrote based on a different thread: https://mcmap.net/q/503759/-grammatically-correct-human-readable-string-from-list-with-oxford-comma I like that it's shorter but I realize that yours is more explicit.Lamrouex
L
0

Based on the accepted answer for the thread you linked to, here's a one-liner that takes an optional argument for whether to use an Oxford comma or not.

from typing import List

def list_items_in_english(l: List[str], oxford_comma: bool = True) -> str:
    """
    Produce a list of the items formatted as they would be in an English sentence.
    So one item returns just the item, passing two items returns "item1 and item2" and
    three returns "item1, item2, and item3" with an optional Oxford comma.
    """
    return ", ".join(l[:-2] + [((oxford_comma and len(l) != 2) * ',' + " and ").join(l[-2:])])
Lamrouex answered 4/11, 2019 at 11:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.