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)