指定:列表,例如[[3,2,1], [3,2,1,4,5], [3,2,1,8,9], [3,2,1,5,7,8,9]]
Todo :找到所有子列表中最长的公共前缀。
存在:在另一个帖子“Common elements between two lists not using sets in Python”中,建议使用“计数器”,它在python 2.7之上可用。但是我们当前的项目是用python 2.6编写的,因此不使用“Counter”。
我目前的代码是这样的:
l = [[3,2,1], [3,2,1,4,5], [3,2,1,8,9], [3,2,1,5,7,8,9]]
newl = l[0]
if len(l)>1:
for li in l[1:]:
newl = [x for x in newl if x in li]
但是我发现它不是非常pythonic,有更好的编码方式吗?
谢谢!
新修改:很抱歉提及:在我的情况下,'l'中列表的共享元素具有相同的顺序,并始终从第0项开始。所以你不会遇到像[[1,2,5,6],[2,1,7]]
答案 0 :(得分:45)
os.path.commonprefix()
适用于列表:)
>>> 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]
答案 1 :(得分:18)
我不确定它是多么pythonic
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))]
答案 2 :(得分:2)
根据您的示例代码,您似乎希望reduce(set.intersection, map(set, l))
版本保留第一个列表的初始顺序。
这需要算法改进,而不是风格改进;单独的“pythonic”代码对你没有任何好处。考虑必须为每个列表中出现的所有值保留的情况:
给定一个列表列表,当且仅当它出现在
nlist
列表中时,每个列表中都会出现一个值,其中nlist
是列表总数。
如果我们可以保证每个值在每个列表中只出现一次,那么上面的内容可以改写:
给定唯一项列表的列表,当且仅当它出现
nlist
次总计时,每个列表中都会出现一个值。
我们可以使用集合来保证列表中的项目是唯一的,因此我们可以将后一种原则与简单的计数策略结合起来:
>>> 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
...
现在我们要做的就是过滤原始列表:
>>> [i for i in l[0] if count[i] == len(l)]
[3, 2, 1]
答案 3 :(得分:2)
这是使用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]
不确定如何考虑“pythonic”。
答案 4 :(得分:0)
效率低,因为一旦发现不匹配就不会提早出现,但它的整洁:
([i for i,(j,k) in enumerate(zip(a,b)) if j!=k] or [0])[0]
答案 5 :(得分:0)
使用生成器表达式和Python 3内置的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)