合并列表项和上一个列表项

时间:2018-08-09 07:21:19

标签: python python-3.x list merge

如果列表项与先前的项不包含某个前缀,我将尝试将它们合并,并在此列表项之间添加\n

prefix  = '!'
cmds    = ['!test','hello','world','!echo','!embed','oh god']

output  = ['!test\nhello\nworld','!echo','!embed\noh god']

我尝试过类似的

for i in list(range(0,len(cmds))):
    if not cmds[i+1].startswith(prefix):
        cmds[i] += cmds.pop(i+1)

但总是会收到list index out of range错误。

我很抱歉,如果措辞不好,或者似乎是一个明显的解决方法,我对python / programming还是陌生的。

编辑:

我设法使其与

一起使用
prefix = '!'
cmds    = ['!test','hello','world','!echo','!embed','oh god']
print(list(range(0,len(cmds))))
for i in reversed(range(len(cmds))):
    if not cmds[i].startswith(prefix):
        cmds[i-1] += '\n'+cmds.pop(i)
print(cmds)

,但是您的答案似乎更加整洁和有效。非常感谢大家

4 个答案:

答案 0 :(得分:8)

我建议创建一个新列表,如您的问题说明中所示:

prefix  = '!'
cmds    = ['!test','hello','world','!echo','!embed','oh god']

output  = []
for cmd in cmds:
    if cmd.startswith(prefix) or not output:
        output.append(cmd)
    else:
        output[-1] += "\n" + cmd  # change the string in the last element of output

结果是:

>>> output
['!test\nhello\nworld', '!echo', '!embed\noh god']

答案 1 :(得分:3)

这是一个使用itertools.groupbyitertools.accumulate的单线解决方案:

from itertools import accumulate, groupby
from operator import itemgetter

x = ['!test','hello','world','!echo','!embed','oh god']

cumsum = accumulate(map(lambda s: s.startswith('!'), x))
result = ['\n'.join(map(itemgetter(0), g)) for _, g in groupby(zip(x, cumsum), itemgetter(1))]

这看起来像两条衬里,因为我想使它清晰易读,但这并不总是必要的:

result = ['\n'.join(map(itemgetter(0), g)) for _, g in groupby(zip(x, accumulate(map(lambda s: s.startswith('!'), x))), itemgetter(1))]

cumsum提供到目前为止找到的以!开头的元素数。这为groupby提供了一个不错的关键。它通过将str.startswith返回的布尔值累加为整数来工作。

最终结果使用cumsum作为键,但是将x的分组元素与换行符连接。

这是一个IDEOne Link

答案 2 :(得分:1)

您可以使用列表理解“也”来做到这一点。

In [1]: cmds    = ['!test','hello','world','!echo','!embed','oh god']
In [2]: prefix  = '!'
In [3]: inds = [i for i, x in enumerate(cmds) if prefix in x]
In [4]: inds.append(len(cmds))
In [5]: lens = list(zip(inds, inds[1:]))
# [(0, 3), (3, 4), (4, 6)]

In [6]: ["\n".join(cmds[a:b]) for a, b in lens]
Out[6]: ['!test\nhello\nworld', '!echo', '!embed\noh god']

答案 3 :(得分:1)

更长的解决方案,但可以使用itertools.groupby轻松推广到其他情况:

from itertools import groupby

class StartGroupOnPrefix:
    def __init__(self, prefix):
        self.output = False
        self.prefix = prefix

    def __call__(self, item):
        if item.startswith(self.prefix):
            self.output = not self.output
        return self.output


prefix  = '!'
cmds    = ['!test','hello','world','!echo','!embed','oh god']

condition = StartGroupOnPrefix(prefix)

out = ['\n'.join(group) for f, group in groupby(cmds, condition)]
print(out)

#  ['!test\nhello\nworld','!echo','!embed\noh god']

因为我们有一个迭代器,所以我们不必一次创建整个输出列表,我们可以即时生成每个输出:

for grouped_item in ('\n'.join(group) for f, group in groupby(cmds, condition)):
    print('-----------\n', grouped_item)

# -----------
#  !test
# hello
# world
# -----------
#  !echo
# -----------
#  !embed
# oh god

一些解释:groupby(iterable)每次从iterable获得不同的项目时都会开始一个新组。每当groupby(iterable, key)函数的返回值更改时,key就会开始一个新组。每当项目以前缀开头时,我们的condition函数就会在TrueFalse之间交替输出。