I have been working on this question in particular. I'm a bit of a python beginner myself, having re-learnt functions just a few days back.
Write a function that takes a list value as an argument and returns a string with all the items separated by a comma and a space, with and inserted before the last item. For example, passing the previous spam list to the function would return 'apples, bananas, tofu, and cats'. But your function should be able to work with any list value passed to it. Be sure to test the case where an empty list [] is passed to your function.
My idea for this was to have my function run a for loop on the range of the number of elements of the list, add the elements of the list to a comma. If the element of the list happens to be the last element, 'and' and that specific element are added together.
Here is the code:
def listsmash(listu):
y = ''
for x in range(len(listu)):
if x == len(listu) -1 :
y+= 'and '+ str(listu[x])
else:
y+= str(listu[x]) + ','
return y
print(listsmash([1,345,34656,456456,456454564]))
Is this an efficient piece of code? Any way for me to make it more efficient than it already is?
1 Answer 1
Nice solution, these are my suggestions:
- Indentation: the first line is not indented correctly, there is an extra tab or spaces before
def listsmash(listu):
. - Code formatting: follow PEP 8 to format the code. You can use pep8online or just use the auto-formatting feature of many IDEs.
- Bug: the output should be
apples, bananas, tofu, and cats
but the code outputsapples,bananas,tofu,and cats
. There should be a space after the comma. - Edge case: if the input list contains only
apples
the code outputsand apples
. This is an edge case to handle, probably at the beginning of the method. - Performance: there is no much to worry about performances, as it runs linearly to the input size. However, the first if condition is executed everytime:
Limit the number of iterations tofor x in range(len(listu)): if x == len(listu) -1 : y+= 'and '+ str(listu[x]) else: y+= str(listu[x]) + ','
len(listy) - 1
and add the last element outside the for-loop:def listsmash(listu): y = '' for x in range(len(listu) - 1): y += str(listu[x]) + ', ' y += 'and ' + str(listu[-1]) return y
- Use str.join: str.join is a handy function in this case:
It joins all elements ofdef listsmash(listu): y = ', '.join(str(value) for value in listu[:-1]) return y + ', and ' + str(listu[-1])
listu
(except the last one) with,
and then we can append the last element at the end. Additionally, the function.join()
is faster than+=
. - Naming: I am not sure what
listu
means, maybe you can consider a better name likeitems
following from the problem description.
Runtime for 1 million items:
Original: 4.321 s
Improved: 0.418 s
Full code:
from random import randint
from time import perf_counter
def original(listu):
y = ''
for x in range(len(listu)):
if x == len(listu) - 1:
y += 'and ' + str(listu[x])
else:
y += str(listu[x]) + ', '
return y
def improved(items):
result = ', '.join(str(item) for item in items[:-1])
return result + ', and ' + str(items[-1])
# Correctness
items = ['apples', 'bananas', 'tofu']
assert original(items) == improved(items)
# Benchmark
n = 1_000_000
items = [randint(0, n) for _ in range(n)]
t0 = perf_counter()
original(items)
total_time = perf_counter() - t0
print(f'Original: {round(total_time, 3)} s')
t0 = perf_counter()
improved(items)
total_time = perf_counter() - t0
print(f'Improved: {round(total_time, 3)} s')
-
\$\begingroup\$ Where exactly should I fix the indentation? \$\endgroup\$Hash– Hash2020年12月07日 07:19:33 +00:00Commented Dec 7, 2020 at 7:19
-
\$\begingroup\$ @HankRyan there is an extra tab before
def listsmash(listu):
. I think it's due to copying and pasting the code in the question, not a big deal. \$\endgroup\$Marc– Marc2020年12月07日 07:29:40 +00:00Commented Dec 7, 2020 at 7:29 -
-
1\$\begingroup\$ And one more thing
str.join with list comprehension faster than str.join with generator
\$\endgroup\$Ch3steR– Ch3steR2020年12月07日 11:50:32 +00:00Commented Dec 7, 2020 at 11:50 -
2\$\begingroup\$ From the SO answer linked
The reason is that .join() needs to make two passes over the data, so it actually needs a real list. If you give it one, it can start its work immediately. If you give it a genexp instead, it cannot start work until it builds-up a new list in memory by running the genexp to exhaustion
@theProgrammer \$\endgroup\$Ch3steR– Ch3steR2020年12月08日 03:34:29 +00:00Commented Dec 8, 2020 at 3:34
'and'
. \$\endgroup\$