Reputation: 441
I'm trying to turn a list into separated strings joined with an ampersand if there are only two items, or commas and an ampersand between the last two e.g.
Jones & Ben
Jim, Jack & James
I currently have this:
pa = ' & '.join(listauthors[search])
and don't know how to make sort out the comma/ampersand issue. Beginner so a full explanation would be appreciated.
Upvotes: 44
Views: 27026
Reputation: 22370
You could use formatted-join
1 to handle joining lists with commas and an ampersand before the final element:
>>> from formatted_join import formatted_join
>>> authors = ['Jim', 'Jack', 'James']
>>> formatted_join(
... items=authors,
... separator=', ',
... final_separator=' & ',
... use_penultimate_separator=False
... )
'Jim, Jack & James'
pip install formatted-join
By default, formatted_join
uses ', '
as the main separator and ' and '
as
the final separator, and will include the penultimate separator only if there
are more than two items in the sequence:
>>> formatted_join(['Hello', 'World'])
'Hello and World'
>>> formatted_join(('One', 'Two', 'Three'))
'One, Two, and Three'
>>> formatted_join(['Apple'])
'Apple'
Additionally the package also provides a few convenience functions for common
joining patterns taking inspiration from the
Intl.ListFormat
API that is available in Javascript (note localized conjunctions and disjunctions have only been added for english and german currently):
formatted_join_conjunction
— Joins items using a comma and localized* “and” before the last item.>>> from formatted_join import formatted_join_conjunction
>>> formatted_join_conjunction(['One', 'Two', 'Three'])
'One, Two, and Three'
>>> formatted_join_conjunction(['One', 'Two'])
'One and Two'
>>> formatted_join_conjunction(['One'])
'One'
>>> formatted_join_conjunction(['Motorcycle', 'Bus', 'Car'], language='en')
'Motorcycle, Bus, and Car'
>>> formatted_join_conjunction(['Motorcycle', 'Bus', 'Car'], language='de')
'Motorcycle, Bus und Car'
formatted_join_disjunction
— Joins items using a comma and localized* “or” before the last item.>>> from formatted_join import formatted_join_disjunction
>>> formatted_join_disjunction(['X', 'Y', 'Z'])
'X, Y, or Z'
>>> formatted_join_disjunction(['Motorcycle', 'Bus', 'Car'], language='en')
'Motorcycle, Bus, or Car'
>>> formatted_join_disjunction(['Motorcycle', 'Bus', 'Car'], language='de')
'Motorcycle, Bus oder Car'
formatted_join_unit
— Joins items with commas only (no distinct final separator).>>> from formatted_join import formatted_join_unit
>>> formatted_join_unit(['A', 'B', 'C'])
'A, B, C'
formatted_join_narrow
— Joins items with a single space separating them.>>> from formatted_join import formatted_join_narrow
>>> formatted_join_narrow(['A', 'B', 'C'])
'A B C'
formatted_join_[conjunction|disjunction]
:language | and | or | separator | has penultimate separator |
---|---|---|---|---|
en | and | or | ', ' |
yes |
de | und | oder | ', ' |
no |
1 Disclosure: I am the author of
formatted-join
. The source code is
avaliable here.
Upvotes: 1
Reputation: 1759
Here's one that supports the following:
def format_list(items, last_delimiter='and', use_oxford_comma=True):
if not items:
return ''
elif len(items) == 1:
return str(items[0])
elif len(items) == 2:
return f'{items[0]} {last_delimiter} {items[1]}'
curated_last_delimiter = f', {last_delimiter} ' if use_oxford_comma else f' {last_delimiter} '
return f'{", ".join(map(str, items[:-1]))}{curated_last_delimiter}{items[-1]}'
assert format_list(['Hi', 12, True]) == 'Hi, 12, and True'
assert format_list(['Hi', 12, True], last_delimiter='&') == 'Hi, 12, & True'
assert format_list(['Hi', 12, True], use_oxford_comma=False) == 'Hi, 12 and True'
assert format_list(['Hi', 12]) == 'Hi and 12'
assert format_list(['Hi']) == 'Hi'
assert format_list([]) == ''
assert format_list(
['bleu', 'rouge', 'vert', 'jaune'],
last_delimiter='et',
use_oxford_comma=False,
) == 'bleu, rouge, vert et jaune'
Upvotes: 0
Reputation: 127
My Idea is as following I dug a bit deeper and tried to make it more flexible as requested.
Concept: Use dictionary to create a person with an attached list of friends.
Advantage: Can contain/store multiple persons with regarding friends. :)
How it works:
5.a) The value n_friend decrements by one with each cycle.
5.b) As long as n_friend does not reach zero every friend will be printed with the argument end="" as explained in point 2.
5.c) After n_friend reaches zero the if/else conditional statment will switch to else and will print the last iteration of the for loop with the requested "and...[enter friend here].". This is without a end="" so the next print will switch into a new line.
.break for code.
friends_list={
"person_1":['Jack', 'John','Jane'],
"person_2":['Luke', 'Darth','Han','Wednesday']
}
for person in friends_list:
print(f"{person} likes following persons: ",end="")
n_friend = len(friends_list[person])
for friend in friends_list[person]:
n_friend -= 1
if n_friend > 0:
print(friend, end=" ")
else:
print(f"and {friend}.")
Upvotes: 0
Reputation: 1660
Just a more grammatically correct example :)
def list_of_items_to_grammatical_text(items):
if len(items) <= 1:
return ''.join(items)
if len(items) == 2:
return ' and '.join(items)
return '{}, and {}'.format(', '.join(items[:-1]), items[-1])
Output example:
l1 = []
l2 = ["one"]
l3 = ["one", "two"]
l4 = ["one", "two", "three"]
list_of_items_to_grammatical_text(l1)
Out: ''
list_of_items_to_grammatical_text(l2)
Out: 'one'
list_of_items_to_grammatical_text(l3)
Out: 'one and two'
list_of_items_to_grammatical_text(l4)
Out: 'one, two, and three'
Upvotes: 3
Reputation: 39
Here is a one line example that handles all the edge cases (empty list, one entry, two entries):
' & '.join(filter(None, [', '.join(my_list[:-1])] + my_list[-1:]))
The filter()
function is used to filter out the empty entries that happens when my_list
is empty or only has one entry.
Upvotes: 3
Reputation: 1
Here's a simple one that also works for empty or 1 element lists:
' and '.join([', '.join(mylist[:-1])]+mylist[-1:])
The reason it works is that for empty lists both [:-1] and [-1:] give us an empty list again
Upvotes: 0
Reputation: 107297
You can simply use Indexng and F-strings (Python-3.6+):
In [1]: l=['Jim','Dave','James','Laura','Kasra']
In [3]: ', '.join(l[:-1]) + f' & {l[-1]}'
Out[3]: 'Jim, Dave, James, Laura & Kasra'
Upvotes: 3
Reputation: 13869
('{}, '*(len(authors)-2) + '{} & '*(len(authors)>1) + '{}').format(*authors)
This solution can handle a list of authors of length > 0, though it can be modified to handle 0-length lists as well. The idea is to first create a format string that we can format by unpacking list. This solution avoids slicing the list so it should be fairly efficient for large lists of authors.
First we concatenate '{}, '
for every additional author beyond two authors. Then we concatenate '{} & '
if there are two or more authors. Finally we append '{}'
for the last author, but this subexpression can be '{}'*(len(authors)>0)
instead if we wish to be able to handle an empty list of authors. Finally, we format our completed string by unpacking the elements of the list using the *
unpacking syntax.
If you don't need a one-liner, here is the code in an efficient function form.
def format_authors(authors):
n = len(authors)
if n > 1:
return ('{}, '*(n-2) + '{} & {}').format(*authors)
elif n > 0:
return authors[0]
else:
return ''
This can handle a list of authors of any length.
Upvotes: 4
Reputation: 3803
It looks like while I was working on my answer, someone may have beaten me to the punch with a similar one. Here's mine for comparison. Note that this also handles cases of 0, 1, or 2 members in the list.
# Python 3.x, should also work with Python 2.x.
def my_join(my_list):
x = len(my_list)
if x > 2:
s = ', & '.join([', '.join(my_list[:-1]), my_list[-1]])
elif x == 2:
s = ' & '.join(my_list)
elif x == 1:
s = my_list[0]
else:
s = ''
return s
assert my_join(['Jim', 'Jack', 'John']) == 'Jim, Jack, & John'
assert my_join(['Jim', 'Jack']) == 'Jim & Jack'
assert my_join(['Jim',]) == 'Jim'
assert my_join([]) == ''
Upvotes: 1
Reputation: 2130
One liner. Just concatenate all but the last element with ,
as the delimiter. Then just append &
and then the last element finally to the end.
print ', '.join(lst[:-1]) + ' & ' + lst[-1]
If you wish to handle empty lists or such:
if len(lst) > 1:
print ', '.join(lst[:-1]) + ' & ' + lst[-1]
elif len(lst) == 1:
print lst[0]
Upvotes: 13
Reputation: 361730
You could break this up into two joins. Join all but the last item with ", "
. Then join this string and the last item with " & "
.
all_but_last = ', '.join(authors[:-1])
last = authors[-1]
' & '.join([all_but_last, last])
Note: This doesn't deal with edge cases, such as when authors
is empty or has only one element.
Upvotes: 16
Reputation: 113988
"&".join([",".join(my_list[:-1]),my_list[-1]])
I would think would work
or maybe just
",".join(my_list[:-1]) +"&"+my_list[-1]
to handle edge cases where only 2 items you could
"&".join([",".join(my_list[:-1]),my_list[-1]] if len(my_list) > 2 else my_list)
Upvotes: 46