Python 如何在排序列表中对具有相似开头的字符串进行分组?
鉴于此:Python 如何在排序列表中对具有相似开头的字符串进行分组?,python,diff,hierarchy,Python,Diff,Hierarchy,鉴于此: ['2014\\2014-01 Jan\\2014-01-01', '2014\\2014-01 Jan\\2014-01-02', '2014\\2014-01 Jan\\2014-01-03', '2014\\2014-01 Jan\\2014-01-04', '2014\\2014-01 Jan\\2014-01-05', '2014\\2014-01 Jan\\2014-01-06', '2014\\2014-01 Jan\\2014-01-07', '2014\
['2014\\2014-01 Jan\\2014-01-01',
'2014\\2014-01 Jan\\2014-01-02',
'2014\\2014-01 Jan\\2014-01-03',
'2014\\2014-01 Jan\\2014-01-04',
'2014\\2014-01 Jan\\2014-01-05',
'2014\\2014-01 Jan\\2014-01-06',
'2014\\2014-01 Jan\\2014-01-07',
'2014\\2014-01 Jan\\2014-01-08',
'2014\\2014-01 Jan\\2014-01-09',
'2014\\2014-01 Jan\\2014-01-10',
'2014\\2014-01 Jan\\2014-01-11',
'2014\\2014-01 Jan\\2014-01-12',
'2014\\2014-01 Jan\\2014-01-13',
'2014\\2014-01 Jan\\2014-01-14',
'2014\\2014-01 Jan\\2014-01-15',
'2014\\2014-01 Jan\\2014-01-16',
'2014\\2014-01 Jan\\2014-01-17',
'2014\\2014-01 Jan\\2014-01-18',
'2014\\2014-01 Jan\\2014-01-19',
'2014\\2014-01 Jan\\2014-01-20',
'2014\\2014-01 Jan\\2014-01-21',
'2014\\2014-01 Jan\\2014-01-22',
'2014\\2014-01 Jan\\2014-01-23',
'2014\\2014-01 Jan\\2014-01-24',
'2014\\2014-01 Jan\\2014-01-25',
'2014\\2014-01 Jan\\2014-01-26',
'2014\\2014-01 Jan\\2014-01-27',
'2014\\2014-01 Jan\\2014-01-28',
'2014\\2014-01 Jan\\2014-01-29',
'2014\\2014-01 Jan\\2014-01-30',
'2014\\2014-01 Jan\\2014-01-31',
'2014\\2014-02 Feb\\2014-02-01',
'2014\\2014-02 Feb\\2014-02-02',
'2014\\2014-02 Feb\\2014-02-03',
'2014\\2014-02 Feb\\2014-02-04',
'2014\\2014-02 Feb\\2014-02-05',
'2014\\2014-02 Feb\\2014-02-06',
'2014\\2014-02 Feb\\2014-02-07',
'2014\\2014-02 Feb\\2014-02-08',
'2014\\2014-02 Feb\\2014-02-09',
'2014\\2014-02 Feb\\2014-02-10',
'2014\\2014-02 Feb\\2014-02-11',
'2014\\2014-02 Feb\\2014-02-12',
'2014\\2014-02 Feb\\2014-02-13',
'2014\\2014-02 Feb\\2014-02-14',
'2014\\2014-02 Feb\\2014-02-15',
'2014\\2014-02 Feb\\2014-02-16',
'2014\\2014-02 Feb\\2014-02-17',
'2014\\2014-02 Feb\\2014-02-18',
'2014\\2014-02 Feb\\2014-02-19']
你是怎么得到这样的东西的?(解决方案1:基于分隔符,具有用户可定义的分隔符)
我经常遇到这种情况,基本上我有一个字符串列表,我希望通过删除字符串开头的冗余匹配元素来简化可视化处理。现在我知道这是正常文件夹遍历的树输出,但这些不是真正的文件夹,只是列表中的字符串
理想情况下,该函数将接受继承人分隔符,或者只接受基于字符的分隔符(separator=None)
字符级层次结构的输出如下:(解决方案2:逐字符)
这在本例中似乎不太有用,但在分析URL、日志等时非常明显。理想情况下,你只需要将相似的部分灰显出来,而不是删除它们,但我甚至不知道如何开始。(或者相反,将差异加粗)。基本上,您是将每个元素与前一个元素进行比较,突出差异,抑制相似性
我已经搜索并找到了许多接近这一点的选项,但不完全是这一点os.path.commonprefix
就是一个例子。也许是difflib
其价值在于减少检查项目列表时的视觉混乱 看来你想重新创造一个 无论如何,这里有一个简单的生成器:
def grouped(iterable):
prefix = None
for i in iterable:
pre, suf = i[:16], i[16:]
if pre != prefix:
prefix = pre
yield pre + suf
else:
yield " " * 16 + suf
def heirarchy(data, separator=","):
for p, x in eachWithPrefix(x.split(separator) if separator else list(x) for x in data):
s = separator.join(x)
c = separator.join(x[:p])
yield ' '*len(c) + s[len(c):]
问得好。这个小型解决方案怎么样:
def commonPrefix(a, b):
i = 0
while i < len(a) and i < len(b) and a[i] == b[i]:
i += 1
return i
def eachWithPrefix(v):
p = ''
for x in v:
yield commonPrefix(p, x), x
p = x
将返回一个值列表,每个值都将说明与前一行相等的字符数,因此
print '\n'.join(' '*p + x[p:] for p, x in eachWithPrefix(v))
将打印您建议的第二个解决方案
print '\n'.join('\t' * p + '\\'.join(x[p:]) for p, x in eachWithPrefix(x.split('\\') for x in v))
另一方面,将对分隔符\
执行相同的操作,并用制表位替换要忽略的部分。这不是您在第一个输出示例中提出的格式,但我想您已经明白了
尝试:
这将用大小相同的空格字符串替换相等的部分。虽然输出仍将包含分隔符,但这可能更好:
2014\2014-01 Jan\2014-01-01
\ \2014-01-02
\ \2014-01-03
\ \2014-01-04
\ \2014-01-05
...
\ \2014-01-31
\2014-02 Feb\2014-02-01
\ \2014-02-02
\ \2014-02-03
...
要同时删除这些内容,您可以使用此方法:
print '\n'.join(' ' * len('\\'.join(x[:p])) + '\\'.join(x)[len('\\'.join(x[:p])):] for p, x in eachWithPrefix(x.split('\\') for x in v))
但这现在包含一些代码加倍,所以这里的迭代循环可能会更好:
for p, x in eachWithPrefix(x.split('\\') for x in v):
s = '\\'.join(x)
c = '\\'.join(x[:p])
print ' '*len(c) + s[len(c):]
或作为易于使用的发电机:
def grouped(iterable):
prefix = None
for i in iterable:
pre, suf = i[:16], i[16:]
if pre != prefix:
prefix = pre
yield pre + suf
else:
yield " " * 16 + suf
def heirarchy(data, separator=","):
for p, x in eachWithPrefix(x.split(separator) if separator else list(x) for x in data):
s = separator.join(x)
c = separator.join(x[:p])
yield ' '*len(c) + s[len(c):]
所以现在
heirarchy(data,separator='\\')
正好创建了您的预期输出。好吧,灵感来自于commonprefix答案,我用它玩了一会儿,当我意识到我每次只能发送两个元素的列表时,灵感来了
这是我的代码,它只处理每个字符的大小写,我不确定这有多好(我怀疑不是很好!因为发生了很多不必要的复制)。但是我成功地重现了我问题的第三个结果。这仍然使另一部分问题悬而未决
def printheirarchy(data,seperator=","):
if len(data) < 2:
pprint(data)
return
newdata = []
newdata.append(data[0])
for i in range(1,len(data)):
prefix = os.path.commonprefix(data[i-1:i+1])
newdata.append(data[i].replace(prefix," "*len(prefix),1))
pprint(newdata)
def PRINTHEARCHY(数据,分隔符=“,”):
如果len(数据)<2:
pprint(数据)
返回
newdata=[]
newdata.append(数据[0])
对于范围(1,len(数据))中的i:
prefix=os.path.commonprefix(数据[i-1:i+1])
newdata.append(数据[i].replace(前缀,“*len(前缀),1))
pprint(新数据)
很有趣。也许如果你让我们看看你的尝试。这可能是一个很好的问题。如果您提供较少的样本数据,可能会改善这个问题。在前10行左右之后,您会得到递减的返回。您真的希望输出为带空格前缀的字符串格式吗?或者这仅仅是为了举例说明?制作一个字典怎么样,其中每个键都是列表中的第一个元素,值是一个按您描述的格式列出的元素列表?我和Erik有相同的问题。将其更改为嵌套字典可能是存储数据的好方法。编辑:@msvalkon-看起来我们在同一时间有相同的想法!按enter键太快,在此可以确认此操作适用于分离器壳体。您在哪里指定分隔符?我能换一下吗?我不知道这是怎么回事。甚至在读过维基百科之后,也无法理解它。你能再解释一下吗?将字符串拆分为16个字符是如何实现所有这些神奇的事情的?你能让它也适用于每个字符的大小写吗?16个字符的拆分“有效”(或者更确切地说“似乎有效”),因为你的通用前缀通常是16个字符长。不过,并非所有情况下都是这样(换日处理不当),所以我必须得出结论,这甚至不是一个有效的解决方案。尽管我已经尝试过,但它确实适用于案例1。但你是说它对其他任何例子都不起作用?这是一个很好的观点,我刚刚测试了这个,这很好!当然,你可以用同样大小的空格串替换列表中的部分,然后把所有的空格连接起来。我会提供这个,请稍等……是的,它现在可以工作了,但是@qarma的答案名称中没有“\”。我使用我的解决方案添加了一些代码,以创建所需的内容。哈哈哈,这正是我现在正在做的,我完成后会向您建议,但您要快得多!我可以确认,你的答案也适用于按字符的情况,但对于分隔符的情况,我不太理解分隔符的情况。假设我只考虑到一个分隔符的相似性,即在分隔符之间,令牌被当作一个整体而不是单独的字符。因此,如果整体不相同,那么它将保持不变。与上面的问题一样,分隔符是“\”(由于字符串转义,显示为“\\”)
for p, x in eachWithPrefix(x.split('\\') for x in v):
s = '\\'.join(x)
c = '\\'.join(x[:p])
print ' '*len(c) + s[len(c):]
def heirarchy(data, separator=","):
for p, x in eachWithPrefix(x.split(separator) if separator else list(x) for x in data):
s = separator.join(x)
c = separator.join(x[:p])
yield ' '*len(c) + s[len(c):]
def printheirarchy(data,seperator=","):
if len(data) < 2:
pprint(data)
return
newdata = []
newdata.append(data[0])
for i in range(1,len(data)):
prefix = os.path.commonprefix(data[i-1:i+1])
newdata.append(data[i].replace(prefix," "*len(prefix),1))
pprint(newdata)
from difflib import SequenceMatcher
def remove_redundant_prefixes(it):
"""
remove_redundant_prefixes(it) -> iterable (generator)
Iterate through a list of strings, removing successive common prefixes.
"""
prev_line = ''
for line in sorted(it):
sm = SequenceMatcher(a=prev_line, b=line)
prev_line = line
# Returns 3 element tuple, last element is the size of the match.
match_size = sm.get_matching_blocks()[0][2]
# No match == no prefix, don't prune the string.
if match_size == 0:
yield line
else:
# Prune per the match
yield line.replace(line[:match_size], ' ' * match_size, 1)