Automate the boring stuff with Python: Comma Code

Solution 1:

Use str.join() to join a sequence of strings with a delimiter. If you do so for all words except for the last, you can insert ' and ' there instead:

def list_thing(words):
    if len(words) == 1:
        return words[0]
    return '{}, and {}'.format(', '.join(words[:-1]), words[-1])

Breaking this down:

  • words[-1] takes the last element of a list. words[:-1] slices the list to produce a new list with all words except the last one.

  • ', '.join() produces a new string, with all strings of the argument to str.join() joined with ', '. If there is just one element in the input list, that one element is returned, unjoined.

  • '{}, and {}'.format() inserts the comma-joined words and the last word into a template (complete with Oxford comma).

If you pass in an empty list, the above function will raise an IndexError exception; you could specifically test for that case in the function if you feel an empty list is a valid use-case for the function.

So the above joins all words except the last with ', ', then adds the last word to the result with ' and '.

Note that if there is just one word, you get that one word; there is nothing to join in that case. If there are two, you get 'word1 and word 2'. More words produces 'word1, word2, ... and lastword'.

Demo:

>>> def list_thing(words):
...     if len(words) == 1:
...         return words[0]
...     return '{}, and {}'.format(', '.join(words[:-1]), words[-1])
...
>>> spam = ['apples', 'bananas', 'tofu', 'cats']
>>> list_thing(spam[:1])
'apples'
>>> list_thing(spam[:2])
'apples, and bananas'
>>> list_thing(spam[:3])
'apples, bananas, and tofu'
>>> list_thing(spam)
'apples, bananas, tofu, and cats'

Solution 2:

I used a different approach. I am a beginner, so I don't know if it's the cleanest way to do it. To me it seemed as the most simple way:

spam = ['apples', 'pizza', 'dogs', 'cats']

def comma(items):
    for i in range(len(items) -2):
        print(items[i], end=", ")# minor adjustment from one beginner to another: to make it cleaner, simply move the ', ' to equal 'end'. the print statement should finish like this --> end=', '
    print(items[-2] + 'and ' + items[-1]) 

comma(spam)

Which will give the output:

apples, pizza, dogs and cats

Solution 3:

Here's a solution that handles the Oxford comma properly. It also copes with an empty list, in which case it returns an empty string.

def list_thing(seq):
    return (' and '.join(seq) if len(seq) <= 2
        else '{}, and {}'.format(', '.join(seq[:-1]), seq[-1]))

spam = ['apples', 'bananas', 'tofu', 'cats']

for i in range(1 + len(spam)):
    seq = spam[:i]
    s = list_thing(seq)
    print(i, seq, repr(s))

output

0 [] ''
1 ['apples'] 'apples'
2 ['apples', 'bananas'] 'apples and bananas'
3 ['apples', 'bananas', 'tofu'] 'apples, bananas, and tofu'
4 ['apples', 'bananas', 'tofu', 'cats'] 'apples, bananas, tofu, and cats'

FWIW, here's a slightly more readable version using an if-else statement instead of a conditional expression:

def list_thing(seq):
    if len(seq) <= 2:
        return ' and '.join(seq)
    else:
        return '{}, and {}'.format(', '.join(seq[:-1]), seq[-1])    

And here's a slightly less readable version, using an f-string:

def list_thing(seq):
    if len(seq) <= 2:
        return ' and '.join(seq)
    else:
        return f"{', '.join(seq[:-1])}, and {seq[-1]}"   

Note that Martijn's code produces 'apples, and bananas' from the 2 item list. My answer is more grammatically correct (in English), however Martijn's is more technically correct because it does exactly what's specified in the OP's quote (although I disagree with his handling of the empty list).

Solution 4:

I tried this, hope this is what you are looking for :-

spam= ['apples', 'bananas', 'tofu', 'cats']

def list_thing(list):

#creating a string then splitting it as list with two items, second being last word
    new_string=', '.join(list).rsplit(',', 1)    

#Using the same method used above to recreate string by replacing the separator.

    new_string=' and'.join(new_string)
    return new_string

print(list_thing(spam))

Solution 5:

My interpretation of the question is such that a single list item would also be the last list item, and as such would need the 'and' inserted before it, as well as a two item list returning both with ', and' between them. Hence no need to deal with single or two item lists seperately, just the first n items, and the last item. I'd also note that while great, a lot of the other items use modules and functions not taught in the Automate the Boring Stuff text by the time the student encounters this question (a student like me had seen join and .format elsewhere, but attempted to only use what had been taught in the text).

def commacode(passedlist):
    stringy = ''
    for i in range(len(passedlist)-1):
        stringy += str(passedlist[i]) + ', '
        # adds all except last item to str
    stringy += 'and ' + str(passedlist[len(passedlist)-1])
    # adds last item to string, after 'and'
    return stringy

And you could handle the empty list case by:

def commacode(passedlist):
    stringy = ''
    try:
        for i in range(len(passedlist)-1):
            stringy += str(passedlist[i]) + ', '
            # adds all except last item to str
        stringy += 'and ' + str(passedlist[len(passedlist)-1])
        # adds last item to string, after 'and'
        return stringy
    except IndexError:
        return '' 
        #handles the list out of range error for an empty list by returning ''