如何在排序列表中对具有类似开头的字符串进行分组?

时间:2014-04-25 13:07:42

标签: 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-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']

我经常遇到这种情况,基本上我有一个字符串列表,我希望通过删除字符串开头的冗余匹配元素来更容易地进行处理。现在我知道这是普通文件夹遍历的TREE输出,但这些不是真正的文件夹,而只是列表中的字符串。

理想情况下,函数会接受一个层次分隔符,或者只是基于字符(seperator = 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 个答案:

答案 0 :(得分:3)

好像你想重新发明http://en.wikipedia.org/wiki/Radix_tree

无论如何,这是一个简单的发电机:

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

答案 1 :(得分:1)

好问题。这个小解决方案怎么样:

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='\\')创建了您预期的输出。

答案 2 :(得分:1)

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)

答案 3 :(得分:0)

好的灵感来自this question的共同前缀答案我用它玩了一会儿,当我意识到我每次只发送一个只有两个元素的列表时就会有灵感!

这是我的代码,它只处理逐个字符的字符,我不确定这有多好(我怀疑不是很多!因为发生了很多不必要的复制)。但我能够成功地从我的问题中重现第三个输出。这仍然使另一部分得不到解决。

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)