Given: a list of lists, such as [[3,2,1], [3,2,1,4,5], [3,2,1,8,9], [3,2,1,5,7,8,9]]
Todo: Find the longest common
I am not sure how pythonic it is
from itertools import takewhile,izip
x = [[3,2,1], [3,2,1,4,5], [3,2,1,8,9], [3,2,1,5,7,8,9]]
def allsame(x):
return len(set(x)) == 1
r = [i[0] for i in takewhile(allsame ,izip(*x))]
os.path.commonprefix() works well for lists :)
>>> x = [[3,2,1], [3,2,1,4,5], [3,2,1,8,9], [3,2,1,5,7,8,9]]
>>> import os
>>> os.path.commonprefix(x)
[3, 2, 1]
Here's an alternative way using itertools:
>>> import itertools
>>> L = [[3,2,1,4], [3,2,1,4,5], [3,2,1,8,9], [3,2,1,5,7,8,9]]
>>> common_prefix = []
>>> for i in itertools.izip(*L):
... if i.count(i[0]) == len(i):
... common_prefix.append(i[0])
... else:
... break
...
>>> common_prefix
[3, 2, 1]
Not sure how "pythonic" it might be considered though.
An modernized Vertical scan solution using a generator expression and Python 3's builtin zip:
lst = [[3,2,1], [3,2,1,1,5], [3,2,1,8,9], [3,2,1,5,7,8,9]]
next(zip(*(x for x in zip(*lst) if len(set(x)) == 1)))
# (3, 2, 1)
See also a related Leetcode problem - Longest Common Prefix.
Given your example code, you seem to want a version of reduce(set.intersection, map(set, l)) that preserves the initial order of the first list.
This requires algorithmic improvements, not stylistic improvements; "pythonic" code alone won't do you any good here. Think about the situation that must hold for all values that occur in every list:
Given a list of lists, a value occurs in every list if and only if it occurs in
nlistlists, wherenlistis the total number of lists.
If we can guarantee that each value occurs only once in every list, then the above can be rephrased:
Given a list of lists of unique items, a value occurs in every list if and only if it occurs
nlisttimes total.
We can use sets to guarantee that the items in our lists are unique, so we can combine this latter principle with a simple counting strategy:
>>> l = [[3,2,1], [3,2,1,4,5], [3,2,1,8,9], [3,2,1,5,7,8,9]]
>>> count = {}
>>> for i in itertools.chain.from_iterable(map(set, l)):
... count[i] = count.get(i, 0) + 1
...
Now all we have to do is filter the original list:
>>> [i for i in l[0] if count[i] == len(l)]
[3, 2, 1]
It is inefficient as it doesn't early-out as soon as a mismatch is found, but its tidy:
([i for i,(j,k) in enumerate(zip(a,b)) if j!=k] or [0])[0]