Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.python > #17280 > unrolled thread
| Started by | Roy Smith <roy@panix.com> |
|---|---|
| First post | 2011-12-15 08:48 -0800 |
| Last post | 2011-12-16 00:26 -0500 |
| Articles | 12 — 7 participants |
Back to article view | Back to comp.lang.python
How to generate "a, b, c, and d"? Roy Smith <roy@panix.com> - 2011-12-15 08:48 -0800
Re: How to generate "a, b, c, and d"? MRAB <python@mrabarnett.plus.com> - 2011-12-15 17:27 +0000
Re: How to generate "a, b, c, and d"? Tim Chase <python.list@tim.thechases.com> - 2011-12-15 12:01 -0600
Re: How to generate "a, b, c, and d"? Ethan Furman <ethan@stoneleaf.us> - 2011-12-15 10:19 -0800
Re: How to generate "a, b, c, and d"? Tim Chase <python.list@tim.thechases.com> - 2011-12-15 12:51 -0600
Re: How to generate "a, b, c, and d"? Roy Smith <roy@panix.com> - 2011-12-15 11:01 -0800
Re: How to generate "a, b, c, and d"? Roy Smith <roy@panix.com> - 2011-12-15 11:01 -0800
Re: How to generate "a, b, c, and d"? MRAB <python@mrabarnett.plus.com> - 2011-12-15 19:27 +0000
Re: How to generate "a, b, c, and d"? Ian Kelly <ian.g.kelly@gmail.com> - 2011-12-15 14:22 -0700
Re: How to generate "a, b, c, and d"? Terry Reedy <tjreedy@udel.edu> - 2011-12-15 19:57 -0500
Re: How to generate "a, b, c, and d"? Chris Angelico <rosuav@gmail.com> - 2011-12-16 13:42 +1100
Re: How to generate "a, b, c, and d"? Terry Reedy <tjreedy@udel.edu> - 2011-12-16 00:26 -0500
| From | Roy Smith <roy@panix.com> |
|---|---|
| Date | 2011-12-15 08:48 -0800 |
| Subject | How to generate "a, b, c, and d"? |
| Message-ID | <9393353.282.1323967703697.JavaMail.geo-discussion-forums@vbyc2> |
I've got a list, ['a', 'b', 'c', 'd']. I want to generate the string, "a, b, c, and d" (I'll settle for no comma after 'c'). Is there some standard way to do this, handling all the special cases? [] ==> '' ['a'] ==> 'a' ['a', 'b'] ==> 'a and b' ['a', 'b', 'c', 'd'] ==> 'a, b, and c' It seems like the kind of thing django.contrib.humanize would handle, but alas, it doesn't.
[toc] | [next] | [standalone]
| From | MRAB <python@mrabarnett.plus.com> |
|---|---|
| Date | 2011-12-15 17:27 +0000 |
| Message-ID | <mailman.3677.1323970009.27778.python-list@python.org> |
| In reply to | #17280 |
On 15/12/2011 16:48, Roy Smith wrote:
> I've got a list, ['a', 'b', 'c', 'd']. I want to generate the string, "a, b, c, and d" (I'll settle for no comma after 'c'). Is there some standard way to do this, handling all the special cases?
>
> [] ==> ''
> ['a'] ==> 'a'
> ['a', 'b'] ==> 'a and b'
> ['a', 'b', 'c', 'd'] ==> 'a, b, and c'
>
> It seems like the kind of thing django.contrib.humanize would handle, but alas, it doesn't.
How about this:
def and_list(items):
if len(items) <= 2:
return " and ".join(items)
return ", ".join(items[ : -1]) + ", and " + items[-1]
print(and_list([]))
print(and_list(['a']))
print(and_list(['a', 'b']))
print(and_list(['a', 'b', 'c']))
print(and_list(['a', 'b', 'c', 'd']))
[toc] | [prev] | [next] | [standalone]
| From | Tim Chase <python.list@tim.thechases.com> |
|---|---|
| Date | 2011-12-15 12:01 -0600 |
| Message-ID | <mailman.3680.1323972100.27778.python-list@python.org> |
| In reply to | #17280 |
On 12/15/11 10:48, Roy Smith wrote:
> I've got a list, ['a', 'b', 'c', 'd']. I want to generate the string, "a, b, c, and d" (I'll settle for no comma after 'c'). Is there some standard way to do this, handling all the special cases?
>
> [] ==> ''
> ['a'] ==> 'a'
> ['a', 'b'] ==> 'a and b'
> ['a', 'b', 'c', 'd'] ==> 'a, b, and c'
>
> It seems like the kind of thing django.contrib.humanize would handle, but alas, it doesn't.
If you have a list, it's pretty easy as MRAB suggests. For
arbitrary iterators, it's a bit more complex. Especially with
the odd edge-case of 2 items where there's no comma before the
conjunction (where >2 has the comma before the conjunction). If
you were willing to forgo the Oxford comma, it would tidy up the
code a bit. Sample code below
-tkc
def gen_list(i, conjunction="and"):
i = iter(i)
first = i.next()
try:
prev = i.next()
except StopIteration:
yield first
else:
more_than_two = False
for item in i:
if not more_than_two: yield first
yield prev
prev = item
more_than_two = True
if more_than_two:
yield "%s %s" % (conjunction, prev)
else:
yield "%s %s %s" % (first, conjunction, prev)
def listify(lst, conjunction="and"):
return ', '.join(gen_list(lst, conjunction))
for test, expected in (
([], ''),
(['a'], 'a'),
(['a', 'b'], 'a and b'),
(['a', 'b', 'c'], 'a, b, and c'),
(['a', 'b', 'c', 'd'], 'a, b, c, and d'),
):
result = listify(test)
print "%r -> %r (got %r) %s" % (
test, expected, result,
result == expected and "PASSED" or "FAILED"
)
[toc] | [prev] | [next] | [standalone]
| From | Ethan Furman <ethan@stoneleaf.us> |
|---|---|
| Date | 2011-12-15 10:19 -0800 |
| Message-ID | <mailman.3689.1323974099.27778.python-list@python.org> |
| In reply to | #17280 |
Tim Chase wrote: > On 12/15/11 10:48, Roy Smith wrote: >> I've got a list, ['a', 'b', 'c', 'd']. I want to generate the string, >> "a, b, c, and d" (I'll settle for no comma after 'c'). Is there some >> standard way to do this, handling all the special cases? >> >> [] ==> '' >> ['a'] ==> 'a' >> ['a', 'b'] ==> 'a and b' >> ['a', 'b', 'c', 'd'] ==> 'a, b, and c' >> >> It seems like the kind of thing django.contrib.humanize would handle, >> but alas, it doesn't. > > If you have a list, it's pretty easy as MRAB suggests. For arbitrary > iterators, it's a bit more complex. Especially with the odd edge-case > of 2 items where there's no comma before the conjunction (where >2 has > the comma before the conjunction). If you were willing to forgo the > Oxford comma, it would tidy up the code a bit. Sample code below <snip> Why go through all that instead of just converting the iterator into a list at the beginning of MRAB's solution and then running with it? ~Ethan~
[toc] | [prev] | [next] | [standalone]
| From | Tim Chase <python.list@tim.thechases.com> |
|---|---|
| Date | 2011-12-15 12:51 -0600 |
| Message-ID | <mailman.3690.1323975113.27778.python-list@python.org> |
| In reply to | #17280 |
On 12/15/11 12:19, Ethan Furman wrote: > Tim Chase wrote: >> On 12/15/11 10:48, Roy Smith wrote: >>> I've got a list, ['a', 'b', 'c', 'd']. I want to generate the string, >>> "a, b, c, and d" (I'll settle for no comma after 'c'). Is there some >>> standard way to do this, handling all the special cases? >> >> If you have a list, it's pretty easy as MRAB suggests. For arbitrary >> iterators, it's a bit more complex. Especially with the odd edge-case >> of 2 items where there's no comma before the conjunction (where>2 has >> the comma before the conjunction). If you were willing to forgo the >> Oxford comma, it would tidy up the code a bit. > > Why go through all that instead of just converting the iterator into a > list at the beginning of MRAB's solution and then running with it? For the fun/challenge? Because you have a REALLY big data source that you don't want to keep in memory (in addition the resulting string)? Yeah, for most non-pathological cases, it would make more sense to just make it a list and then deal with the 4 cases (no elements, one element, 2 elements, and >2 elements) individually. -tkc
[toc] | [prev] | [next] | [standalone]
| From | Roy Smith <roy@panix.com> |
|---|---|
| Date | 2011-12-15 11:01 -0800 |
| Message-ID | <mailman.3691.1323975683.27778.python-list@python.org> |
| In reply to | #17305 |
FWIW, I ended up with:
n = len(names)
if n == 0:
return ''
if n == 1:
return names[0]
pre = ', '.join(names[:-1])
post = names[-1]
return '%s, and %s' (pre, post)
the slice-and-join() takes care of both the 2 and >2 element cases at the same time :)
It would be nice if there were some standard way to do this. I'm sure I've seen something that was essentially a join() that took two delimiters; one for most elements, the other a special-case for the last one. I can't remember where I saw it. I'm guessing in some web framework.
[toc] | [prev] | [next] | [standalone]
| From | Roy Smith <roy@panix.com> |
|---|---|
| Date | 2011-12-15 11:01 -0800 |
| Message-ID | <19419800.394.1323975674835.JavaMail.geo-discussion-forums@vbyc2> |
| In reply to | #17305 |
FWIW, I ended up with:
n = len(names)
if n == 0:
return ''
if n == 1:
return names[0]
pre = ', '.join(names[:-1])
post = names[-1]
return '%s, and %s' (pre, post)
the slice-and-join() takes care of both the 2 and >2 element cases at the same time :)
It would be nice if there were some standard way to do this. I'm sure I've seen something that was essentially a join() that took two delimiters; one for most elements, the other a special-case for the last one. I can't remember where I saw it. I'm guessing in some web framework.
[toc] | [prev] | [next] | [standalone]
| From | MRAB <python@mrabarnett.plus.com> |
|---|---|
| Date | 2011-12-15 19:27 +0000 |
| Message-ID | <mailman.3694.1323977203.27778.python-list@python.org> |
| In reply to | #17280 |
On 15/12/2011 18:51, Tim Chase wrote: > On 12/15/11 12:19, Ethan Furman wrote: >> Tim Chase wrote: >>> On 12/15/11 10:48, Roy Smith wrote: >>>> I've got a list, ['a', 'b', 'c', 'd']. I want to generate the >>>> string, "a, b, c, and d" (I'll settle for no comma after 'c'). >>>> Is there some standard way to do this, handling all the special >>>> cases? >>> >>> If you have a list, it's pretty easy as MRAB suggests. For >>> arbitrary iterators, it's a bit more complex. Especially with >>> the odd edge-case of 2 items where there's no comma before the >>> conjunction (where>2 has the comma before the conjunction). If >>> you were willing to forgo the Oxford comma, it would tidy up the >>> code a bit. >> >> Why go through all that instead of just converting the iterator >> into a list at the beginning of MRAB's solution and then running >> with it? > > For the fun/challenge? Because you have a REALLY big data source > that you don't want to keep in memory (in addition the resulting > string)? > > Yeah, for most non-pathological cases, it would make more sense to > just make it a list and then deal with the 4 cases (no elements, one > element, 2 elements, and>2 elements) individually. > I was going to question it too, but then I wondered what would happen if there were a very large number of items and the string would be too big for memory, for example, writing a list of all the numbers from one to a billion to a file.
[toc] | [prev] | [next] | [standalone]
| From | Ian Kelly <ian.g.kelly@gmail.com> |
|---|---|
| Date | 2011-12-15 14:22 -0700 |
| Message-ID | <mailman.3704.1323984189.27778.python-list@python.org> |
| In reply to | #17280 |
On Thu, Dec 15, 2011 at 11:51 AM, Tim Chase <python.list@tim.thechases.com> wrote: > For the fun/challenge? Because you have a REALLY big data source that you > don't want to keep in memory (in addition the resulting string)? If you have that much data, then I question why you would want to build such a large human-readable list in the first place. Nobody is going to want to read that no matter how you format it.
[toc] | [prev] | [next] | [standalone]
| From | Terry Reedy <tjreedy@udel.edu> |
|---|---|
| Date | 2011-12-15 19:57 -0500 |
| Message-ID | <mailman.3708.1323997066.27778.python-list@python.org> |
| In reply to | #17280 |
On 12/15/2011 12:27 PM, MRAB wrote:
> On 15/12/2011 16:48, Roy Smith wrote:
>> I've got a list, ['a', 'b', 'c', 'd']. I want to generate the string,
>> "a, b, c, and d" (I'll settle for no comma after 'c'). Is there some
>> standard way to do this, handling all the special cases?
>>
>> [] ==> ''
>> ['a'] ==> 'a'
>> ['a', 'b'] ==> 'a and b'
>> ['a', 'b', 'c', 'd'] ==> 'a, b, and c'
>>
>> It seems like the kind of thing django.contrib.humanize would handle,
>> but alas, it doesn't.
>
> How about this:
>
> def and_list(items):
> if len(items) <= 2:
> return " and ".join(items)
>
> return ", ".join(items[ : -1]) + ", and " + items[-1]
To avoid making a slice copy,
last = items.pop()
return ", ".join(items) + (", and " + last)
I parenthesized the last two small items to avoid copying the long
string twice with two appends. Even better is
items[-1] = "and " + items[-1]
return ", ".join(items)
so the entire output is created in one operation with no copy.
But I would only mutate the list if I started with
items = list(iterable)
where iterable was the input, so I was mutating a private copy.
--
Terry Jan Reedy
[toc] | [prev] | [next] | [standalone]
| From | Chris Angelico <rosuav@gmail.com> |
|---|---|
| Date | 2011-12-16 13:42 +1100 |
| Message-ID | <mailman.3710.1324003340.27778.python-list@python.org> |
| In reply to | #17280 |
On Fri, Dec 16, 2011 at 11:57 AM, Terry Reedy <tjreedy@udel.edu> wrote: > items[-1] = "and " + items[-1] > return ", ".join(items) This works only if you're sure there are at least two items, and if you don't mind two items coming out as "a, and b". ChrisA
[toc] | [prev] | [next] | [standalone]
| From | Terry Reedy <tjreedy@udel.edu> |
|---|---|
| Date | 2011-12-16 00:26 -0500 |
| Message-ID | <mailman.3714.1324013192.27778.python-list@python.org> |
| In reply to | #17280 |
On 12/15/2011 9:42 PM, Chris Angelico wrote: > On Fri, Dec 16, 2011 at 11:57 AM, Terry Reedy<tjreedy@udel.edu> wrote: >> items[-1] = "and " + items[-1] >> return ", ".join(items) > > This works only if you're sure there are at least two items, and if > you don't mind two items coming out as "a, and b". Please read the context that you removed. The original second return line and my replacements come after if len(items) <= 2: return " and ".join(items) -- Terry Jan Reedy
[toc] | [prev] | [standalone]
Back to top | Article view | comp.lang.python
csiph-web