Python在开始和结束关键字模式中将列表拆分为子列表

时间:2018-04-01 09:20:08

标签: python list split sublist

如果我有一个清单,请说:

lst = ['foo', 'bar', '!test', 'hello', 'world!', 'word']

字符为!,如何返回给出的列表:

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

我很难找到解决方案。这是我尝试过的一种方法:

def define(lst):
    for index, item in enumerate(lst):
        if item[0] == '!' and lst[index+2][-1] == '!':
            temp = lst[index:index+3]
            del lst[index+1:index+2]
            lst[index] = temp
    return lst

非常感谢任何帮助。

6 个答案:

答案 0 :(得分:12)

假设没有任何元素可以启动&与!类似的'!foo!'结束。

首先,我们可以编写像

这样的辅助谓词
def is_starting_element(element):
    return element.startswith('!')


def is_ending_element(element):
    return element.endswith('!')

然后我们可以写generator-function(因为它们很棒)

def walk(elements):
    elements = iter(elements)  # making iterator from passed iterable
    for position, element in enumerate(elements):
        if is_starting_element(element):
            yield [element[1:], *walk(elements)]
        elif is_ending_element(element):
            yield element[:-1]
            return
        else:
            yield element

试验:

>>> lst = ['foo', 'bar', '!test', 'hello', 'world!', 'word']
>>> list(walk(lst))
['foo', 'bar', ['test', 'hello', 'world'], 'word']
>>> lst = ['foo', 'bar', '!test', '!hello', 'world!', 'word!']
>>> list(walk(lst))
['foo', 'bar', ['test', ['hello', 'world'], 'word']]
>>> lst = ['hello!', 'world!']
>>> list(walk(lst))
['hello']

从上一个例子可以看出,如果有更多的关闭元素而不是打开关闭元素,那么关闭元素将被忽略(这是因为我们从生成器中return。因此,如果lst具有无效签名(开始和结束元素之间的差异不等于零),那么我们可能会有一些不可预测的行为。作为摆脱这种情况的一种方法,我们可以在处理之前验证给定的数据,并在数据无效时引发错误。

我们可以编写像

这样的验证器
def validate_elements(elements):
    def get_sign(element):
        if is_starting_element(element):
            return 1
        elif is_ending_element(element):
            return -1
        else:
            return 0

    signature = sum(map(get_sign, elements))
    are_elements_valid = signature == 0
    if not are_elements_valid:
        error_message = 'Data is invalid: '
        if signature > 0:
            error_message += ('there are more opening elements '
                              'than closing ones.')
        else:
            error_message += ('there are more closing elements '
                              'than opening ones.')
        raise ValueError(error_message)

测试

>>> lst = ['!hello', 'world!']
>>> validate_elements(lst)  # no exception raised, data is valid
>>> lst = ['!hello', '!world']
>>> validate_elements(lst)
...
ValueError: Data is invalid: there are more opening elements than closing ones.
>>> lst = ['hello!', 'world!']
>>> validate_elements(lst)
...
ValueError: Data is invalid: there are more closing elements than opening ones.

最后我们可以编写像

这样的验证函数
def to_sublists(elements):
    validate_elements(elements)
    return list(walk(elements))

测试

>>> lst = ['foo', 'bar', '!test', 'hello', 'world!', 'word']
>>> to_sublists(lst)
['foo', 'bar', ['test', 'hello', 'world'], 'word']
>>> lst = ['foo', 'bar', '!test', '!hello', 'world!', 'word!']
>>> to_sublists(lst)
['foo', 'bar', ['test', ['hello', 'world'], 'word']]
>>> lst = ['hello!', 'world!']
>>> to_sublists(lst)
...
ValueError: Data is invalid: there are more closing elements than opening ones.

修改

如果我们想要处理启动和启动的元素以[{1}} !结尾,我们可以使用itertools.chain修改'!bar!'功能

walk

我们还需要通过修改from itertools import chain def walk(elements): elements = iter(elements) for position, element in enumerate(elements): if is_starting_element(element): yield list(walk(chain([element[1:]], elements))) elif is_ending_element(element): element = element[:-1] yield element return else: yield element 函数

来完成验证
get_sign

测试

def get_sign(element):
    if is_starting_element(element):
        if is_ending_element(element):
            return 0
        return 1
    if is_ending_element(element):
        return -1
    return 0

答案 1 :(得分:4)

这是一个可以处理任意嵌套列表的迭代解决方案:

def nest(lst, sep):
    current_list = []
    nested_lists = [current_list]  # stack of nested lists
    for item in lst:
        if item.startswith(sep):
            if item.endswith(sep):
                item = item[len(sep):-len(sep)]  # strip both separators
                current_list.append([item])
            else:
                # start a new nested list and push it onto the stack
                new_list = []
                current_list.append(new_list)
                current_list = new_list
                nested_lists.append(current_list)
                current_list.append(item[len(sep):])  # strip the separator
        elif item.endswith(sep):
            # finalize the deepest list and go up by one level
            current_list.append(item[:-len(sep)])  # strip the separator
            nested_lists.pop()
            current_list = nested_lists[-1]
        else:
            current_list.append(item)

    return current_list

试运行:

>>> nest(['foo', 'bar', '!test', '!baz!', 'hello', 'world!', 'word'], '!')
['foo', 'bar', ['test', ['baz'], 'hello', 'world'], 'word']

它的工作方式是维护一堆嵌套列表。每次创建新的嵌套列表时,它都会被压入堆栈。元素始终附加到堆栈中的最后一个列表。当一个元素以"!"结尾时找到后,最上面的列表将从堆栈中删除。

答案 2 :(得分:2)

我首先要确定子列表的起点和终点在哪里,然后相应地删除列表,然后删除!

def define(lst):
    # First find the start and end indexes
    for index, item in enumerate(lst):
        if item[0] == '!':
            start_index = index
        if item[-1] == "!":
            end_index = index+1

    # Now create the new list
    new_list = lst[:start_index] + [lst[start_index:end_index]] + lst[end_index:]

    # And remove the !s
    new_list[start_index][0] = new_list[start_index][0][1:]
    new_list[start_index][-1] = new_list[start_index][-1][:-1]

    return new_list

答案 3 :(得分:2)

这是一个非常简单的实现:

dropDB

首先,我们将lst = ['foo', 'bar', '!test', 'hello', 'world!', 'word'] lst_tmp = [(tuple(el.split()) if ' ' in (el[0], el[-1]) else el.split()) for el in ' '.join(lst).split('!')] lst = [] for el in lst_tmp: if isinstance(el, tuple): for word in el: lst.append(word) else: lst.append(el) 加入一个lst,然后将其拆分为str。现在,这会产生'!'。我们现在可以在元素的开头或结尾使用出现的空白字符来表示嵌入的['foo bar ', 'test hello world', ' word']应该出现的位置。单独出现的单词会打包到list中,只是为了将它们与tuple(s)区分开来。所有这些都导致list。最后要做的是将lst_tmp解压缩到它们的单个元素中,这就是循环正在做的事情。

答案 4 :(得分:0)

我认为你应该插入数组而不是分配它。你还需要删除索引+ 3

def define(lst):
    for index, item in enumerate(lst):
        if item[0] == '!' and lst[index+2][-1] == '!':
            temp = lst[index:index+3]
            del lst[index:index+3]
            lst.insert(index, temp)
    return lst

答案 5 :(得分:-1)

请尝试以下:

lst = ['foo', 'bar', '!test', 'hello', 'world!', 'word']
temp =[]
isFound=False
for str in lst:
    if str.startswith("!"):
        temp.append(str,replace("!",""))
        isFound=True
    elif len(temp) and isFound and not str.endswith("!"):
        temp.append(str)
    elif str.endswith("!"):
        temp.append(str,replace("!",""))
        isFound=False
for item in temp:
    lst.remove(item)
lst.append(temp)