Python将列表按给定的开始/结束关键字拆分为子列表

15
如果我要列出一个列表,比如说:
lst = ['hello', 'foo', 'test', 'world', 'bar', 'idk']

我希望将它拆分为一个子列表,以'foo''bar'作为起始和结束关键字,这样我就可以得到

lst = ['hello', ['foo', 'test', 'world', 'bar'], 'idk']

我目前的做法如下。
def findLoop(t):   
    inds = [index for index, item in enumerate(t) if item in ["FOO", "BAR"]]
    centre = inds[(len(inds)/2)-1:(len(inds)/2)+1]
    newCentre = t[centre[0]:centre[1]+1]
    return t[:centre[0]] + [newCentre] + t[centre[1]+1:]

def getLoops(t):
    inds = len([index for index, item in enumerate(t) if item in ["FOO", "BAR"]])
    for i in range(inds):
        t = findLoop(t)
    return t

这看起来有点凌乱,但它非常适用于嵌套的开始/结束关键字,因此子列表可以在子列表内部形成,但对于不在彼此内部的多个开始/结束关键字则无法正常工作。目前尚未重要的是嵌套,因此任何帮助都将不胜感激。

5个回答

12

使用切片的一种方法:

>>> lst = ['hello', 'foo', 'test', 'world', 'bar', 'idk']
>>> a=lst.index('foo')    # locate start word
>>> b=lst.index('bar')+1  # locate end word
>>> lst[a:b] = [lst[a:b]] # replace list slice with a list of the slice
>>> lst
['hello', ['foo', 'test', 'world', 'bar'], 'idk']

1
是的,但OP也要求多个起始和结束。我冒昧地将其制作成一个函数。 - Anton vBR
2
@AntonvBR 的例子无法运行,也没有展示他的意思。请添加您自己的答案,不要编辑我的答案。 - Mark Tolonen

8
多个开始和结束点(基于Mark Tolonen的答案)
lst = ['hello', 'foo', 'test', 'world', 'bar', 'idk','am']
t = [('foo','test'),('world','idk')]

def sublists(lst, t):
    for start,end in t:
        a=lst.index(start)
        b=lst.index(end)+1
        lst[a:b] = [lst[a:b]]
    return lst

print(sublists(lst,t)) 

返回:

 ['hello', ['foo', 'test'], ['world', 'bar', 'idk'], 'am']

3
在Op案例中,如果出现多次就无法实现。例如:['A', 'foo', 'test', 'bar', 'B', 'foo', 'test2', 'bar'] 应该变成 ['A', ['foo', 'test', 'bar'], 'B', ['foo', 'test2', 'bar']] 而不是 ['A', ['foo', 'test', 'bar'], 'B', 'foo', 'test2', 'bar']。对于['A', 'foo', 'bar', 'bar'],它应该分组为['foo', 'bar', 'bar'](与您的代码相同)还是['foo', 'bar'] - Giacomo Alzetta
1
相关但不清楚原帖作者打算如何使用它。当然,你也可以构建错误异常。总之是一个很好的观点! - Anton vBR

1
使用切片,不支持嵌套列表:
>>> lst = ['hello', 'foo', 'test', 'world', 'bar', 'idk']
>>> start_idx = lst.index('foo')
>>> end_idx = lst.index('bar')
>>> lst[:start_idx] + [lst[start_idx:end_idx+1]] + lst[end_idx+1:]
['hello', ['foo', 'test', 'world', 'bar'], 'idk']

1

一种有创意的方法是将您的列表转储为 JSON字符串,根据需要添加[],然后将JSON字符串解析回Python嵌套列表:

import json
lst = ['hello', 'foo', 'test', 'world', 'bar', 'idk']
start_keywords = ['world', 'foo', 'test']
end_keywords = ['bar', 'idk', 'foo']
dump = json.dumps(lst)

for k in start_keywords:
    dump = dump.replace(f'"{k}"', f'["{k}"')

for k in end_keywords:
    dump = dump.replace(f'"{k}"', f'"{k}"]')

json.loads(dump)
# ['hello', ['foo'], ['test', ['world', 'bar'], 'idk']]
json.loads(dump)[2][1][0]
# 'world'

优点是易于跟随,对任意嵌套列表都能正常工作,并且它可以检测结构是否正确。但是,你需要确保你的单词不包含 "

1
哇,这个完美地运行了,甚至包括我计划以后实现的东西。谢谢! - Leo Whitehead

1
要使您的代码达到预期的结果,您需要进行以下更改:
  1. Slice indices must be integers. Your findLoop function fails on the second line if your test list has an odd length. Coerce the type of the slice indices to int to round down (as is required here)

    centre = inds[int(len(inds)/2)-1:int(len(inds)/2)+1]
    
  2. in is case sensitive.

    >>> 'foo' in ['FOO', 'BAR']
    False
    
  3. In getLoops, you only need to search for the first element in your pair, as findLoops sublists from a pair of words on each call.

    inds = len([index for index, item in enumerate(t) if item in ['foo']])
    

在线尝试!


然而,正如您所注意到的,您的代码非常混乱,其他答案展示了如何更好地使用list().index()。如果您想进一步查找嵌套子列表,则需要对您希望其如何运作进行更多澄清。请考虑以下问题: - 对于子列表['foo', 'bar'],然后是['test','world'],是否只应在初始列表上进行子列表处理,还是在子列表中也应该进行? - 对于子列表['foo', 'world'],然后是['test','bar'],不同级别的列表上的匹配项应该如何处理?

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接