如何在排序列表中将具有相似开头的字符串分组?
给定这个:
['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:基于分隔符,用户可以自定义分隔符)
['2014\\2014-01 Jan\\2014-01-01',
' \\2014-01-02',
' \\2014-01-03',
' \\2014-01-04',
' \\2014-01-05',
' \\2014-01-06',
' \\2014-01-07',
' \\2014-01-08',
' \\2014-01-09',
' \\2014-01-10',
' \\2014-01-11',
' \\2014-01-12',
' \\2014-01-13',
' \\2014-01-14',
' \\2014-01-15',
' \\2014-01-16',
' \\2014-01-17',
' \\2014-01-18',
' \\2014-01-19',
' \\2014-01-20',
' \\2014-01-21',
' \\2014-01-22',
' \\2014-01-23',
' \\2014-01-24',
' \\2014-01-25',
' \\2014-01-26',
' \\2014-01-27',
' \\2014-01-28',
' \\2014-01-29',
' \\2014-01-30',
' \\2014-01-31',
' \\2014-02 Feb\\2014-02-01',
' \\2014-02-02',
' \\2014-02-03',
' \\2014-02-04',
' \\2014-02-05',
' \\2014-02-06',
' \\2014-02-07',
' \\2014-02-08',
' \\2014-02-09',
' \\2014-02-10',
' \\2014-02-11',
' \\2014-02-12',
' \\2014-02-13',
' \\2014-02-14',
' \\2014-02-15',
' \\2014-02-16',
' \\2014-02-17',
' \\2014-02-18',
' \\2014-02-19']
我经常遇到这种情况,基本上我有一串字符串,想通过去掉开头重复的部分来让它们更容易看懂。现在我知道,这种树形输出是用来正常浏览文件夹的,但这些并不是真正的文件夹,只是列表中的字符串。
理想情况下,这个功能应该能接受一个层级分隔符,或者直接按字符处理(分隔符=None)。
def printheirarchy(data,seperator=","):
字符级别的输出应该像这样:(解决方案2:逐字符处理)
['2014\\2014-01 Jan\\2014-01-01',
' 2',
' 3',
' 4',
' 5',
' 6',
' 7',
' 8',
' 9',
' 10',
' 1',
' 2',
' 3',
' 4',
' 5',
' 6',
' 7',
' 8',
' 9',
' 20',
' 1',
' 2',
' 3',
' 4',
' 5',
' 6',
' 7',
' 8',
' 9',
' 30',
' 1',
' 2 Feb\\2014-02-01',
' 2',
' 3',
' 4',
' 5',
' 6',
' 7',
' 8',
' 9',
' 10',
' 1',
' 2',
' 3',
' 4',
' 5',
' 6',
' 7',
' 8',
' 9']
在这个例子中,这似乎没什么用,但在分析网址、日志等时就很明显了。理想情况下,你可以把相似的部分变成灰色,而不是直接去掉它们,但我甚至不知道该从哪里开始。(或者反过来,把不同的部分加粗)。基本上,你是在比较每个元素和前一个元素,突出显示不同的地方,同时抑制相似的部分。
我搜索过,发现很多接近这个的选项,但没有完全符合的。os.path.commonprefix就是一个例子。也许可以用difflib?
这样做的价值在于减少在查看项目列表时的视觉杂乱。
相关问题:
4 个回答
好的,受到这个问题中关于公共前缀的回答的启发,我玩了一会儿,灵感来了,我意识到每次可以只发送一个包含两个元素的列表!
这是我的代码,这段代码只处理逐个字符的情况,我不太确定这个方法好不好(我怀疑效果不太好!因为会有很多不必要的复制)。不过我成功地重现了我问题中的第三个输出。至于其他部分,还是没有解决。
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)
在编程中,有时候我们需要处理一些数据,这些数据可能来自不同的地方,比如用户输入、文件或者网络请求。为了让程序能够理解和使用这些数据,我们通常会将它们转换成一种统一的格式。
这种转换的过程就叫做“解析”。简单来说,就是把复杂的数据结构变得简单易懂。比如说,如果你收到了一段文本,里面包含了很多信息,你需要把这些信息提取出来,才能在程序中使用。
解析的方式有很多种,具体取决于数据的格式。有些数据是以JSON格式存储的,这是一种很常见的轻量级数据交换格式,易于人阅读和编写,也容易被机器解析和生成。
在解析数据时,我们可能会用到一些工具或者库,这些工具可以帮助我们快速而准确地完成解析工作。这样,我们就可以把注意力集中在程序的其他部分,而不是花时间去处理数据的细节。
总之,解析就是把复杂的数据变简单,让程序能够轻松使用这些数据。
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)
看起来你想要重新发明一种叫做基数树的东西。
无论如何,这里有一个简单的生成器:
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 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
现在你可以选择你想要的内容:
list(eachWithPrefix(v))
这个会返回一个你的值的列表,每个值会说明有多少个字符和前一行相等,所以
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))
另一方面,这个会对分隔符\执行相同的操作,并把要省略的部分替换成制表符。虽然这和你在第一个输出示例中提出的格式不完全一样,但我想你明白我的意思。
试试:
print '\n'.join('\\'.join([ s if i >= p else ' '*len(s) for i, s in enumerate(x) ]) 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 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='\\')就能生成你期待的输出了。