Find common substring that starts a set of strings How would you in a pythonic manner write a function that returns the common substring found at the beginning of a set of strings? This is my ugly attempt:

def common_start(s):
   l = len(s)
   if l == 0: return None
   elif l == 1: return next(iter(s))
   else:
     i = iter(s)
     start = next(i)
     while start != "":
       if all(ii.startswith(start) for ii in i):
           return start
       else:
         i = iter(s)
         start = start[:-1]
         next(i) # were comparing againt the first item, so we don't need it
     else:
       return ""

s = set(("abc1","abcd","abc","abc abc")) 
common_start(s) # "abc"

s = set(("ab","abcd","abc","abc abc")) 
common_start(s) # "ab"

s = set(("ab",)) 
common_start(s) # "ab"

s = set(("","abcd","abc","abc abc")) 
common_start(s) # ""

s = set() 
common_start(s) # None
You should read PEP 8 and get a linter; pycodestyle, pylint, flake8, prospector, coala. It doesn't matter which one, just get one and use it.


Indenting with 3 spaces is awful, no-one can easily interact with your code.
I've never in my life seen 3 spaces for indentation, it makes me think your post is a joke.
Variables like s, l and  ii are useless.
It's really bad to put statements on the same line as other statements.


  if l == 0: return None



The amount of times I've just not seen the return None with lines like this isn't funny. It's nearly 100%.





Change your if, elif, else to guard statements and you can dedent your code.
There's no need for while else, you've not broken from the loop.
Returning None is just odd at best, change it to an empty string.
Taking a set as input is really strange, it causes you to have to do next(iter(s)). And you're not even exploiting the benefits of it.
Using iterators is clever, but not smart. You can just make a new list to contain all but the first value.

Additionally it doesn't really matter if you check against the first value because it's always going to be a subset of itself. Just seems like adding complexity for the sake of complexity.
You can remove the if l == 1 check.


def common_start(values):
    if len(values) != 0:
        start = values[0]
        while start:
            if all(value.startswith(start) for value in values):
                return start
            start = start[:-1]
    return ""




However this isn't the best. If there are \$s\$ values, and each have a length \$l\$ then your code runs in \$O(sl^2)\$ time.

Instead I'd suggest checking the first value against everything once - running in \$s\$ time. Since you do this \$l\$ times it runs in \$O(sl)\$ time.

To do so you can use zip to iterate through all the first characters, then second characters at the same time. From this you can check if they are the same and append them to a temporary list. Finally you return the contents of the list converted to one single string. In the end you would end up with something like salparadise created.

Again I would still make an empty list or set as input return an empty string.

def common(values):
    start = []
    for characters in zip(*values):
        if len(set(characters)) != 1:
            break
        start.append(characters[0])
    return ''.join(start)