Your usage of iter()
and next()
is unnecessarily confusing. This comes from the requirement of using a set
as an input, where sets are not indexable.
If you passed in a list, then instead of this:
elif l == 1: return next(iter(s))
you could write:
elif l == 1: return s[0]
which is much clearer. It gets even clearer when you remove the iterator from the all( ... for ii in i)
, which necessitates using i = iter(s); next(i)
to reset the iterator. Here is the updated code:
def common_start(*s):
l = len(s)
if l == 0:
return None
elif l == 1:
return s[0]
start = s[0]
while start != "" and not all(ii.startswith(start) for ii in s[1:]):
start = start[:-1]
return start
Usage:
>>> s = { "abc1", "abcd", "abc", "abc abc" }
>>> common_start(*s)
'abc'
The *s
explodes the set into a list of arguments.
Reduce
What is the common prefix among N strings? It would be the same as taking the common prefix of the first two strings, and using that compute the common prefix with the third string, and so on, until you reach the last string.
common_start({a, b, c, d}) == common_prefix(common_prefix(common_prefix(a, b), c), d)
Which leads us to functools.reduce()
.
from functools import reduce
def common_start(s):
if s:
return reduce(common_prefix, s)
return None
Now, we just need a function to return the common prefix of two strings.
As an example, this works, although is a bit cryptic. You might be able to come up with a simpler, possibly faster method:
from itertools import takewhile
def common_prefix(a, b):
return a[:len(list(takewhile((lambda z: z[0] == z[1]), zip(a, b))))]
os.path
Surprisingly, Python comes with the required function built-in to the os.path
module. Just need to convert the set
to a list
, and handle the empty set to None
special case:
import os
def common_start(s):
if s:
return os.path.commonprefix(list(s))
return None
common_start({}) == None
instead of""
? \$\endgroup\$